views:

1238

answers:

5

Hello everybody,

Do you know an easy and straight-forward method/sub/module which allows me to convert a number (say 1234567.89) to an easily readable form - something like 1.23M?

Right now I can do this by making several comparisons, but I'm not happy with my method:

if($bytes > 1000000000){ 
   $bytes = ( sprintf( "%0.2f", $bytes/1000000000 )). " Gb/s";                   
}
elsif ($bytes > 1000000){       
   $bytes = ( sprintf( "%0.2f", $bytes/1000000 )). " Mb/s"; 
}
elsif ($bytes > 1000){
   $bytes = ( sprintf( "%0.2f", $bytes/1000 )). " Kb/s"; 
}
else{ 
   $bytes = sprintf( "%0.2f", $bytes ). "b/s";
}                                                                  

Thank you for your help!

+17  A: 

The Number::Bytes::Human module should be able to help you out.

An example of how to use it can be found in its synopsis:

  use Number::Bytes::Human qw(format_bytes);

  $size = format_bytes(0); # '0'
  $size = format_bytes(2*1024); # '2.0K'

  $size = format_bytes(1_234_890, bs => 1000); # '1.3M'
  $size = format_bytes(1E9, bs => 1000); # '1.0G'

  # the OO way
  $human = Number::Bytes::Human->new(bs => 1000, si => 1);
  $size = $human->format(1E7); # '10MB'
  $human->set_options(zero => '-');
  $size = $human->format(0); # '-'
MikeCroucher
+2  A: 

Number::Bytes::Human seems to do exactly what you want.

Swaroop C H
+1  A: 

In pure Perl form, I've done this with a nested ternary operator to cut on verbosity:

sub BytesToReadableString($) {
   my $c = shift;
   $c >= 1073741824 ? sprintf("%0.2fGB", $c/1073741824)
      : $c >= 1048576 ? sprintf("%0.2fMB", $c/1048576)
      : $c >= 1024 ? sprintf("%0.2fKB", $c/1024)
      : $c . "bytes";
}

print BytesToReadableString(225939) . "/s\n";

Outputs:

220.64KB/s
spoulson
What's with the superfluous scalar($c)?
dland
The scalar function is a typecasts. It's an easy way to convert an integer to a string. It also removes ambiguity between text and numeric operations.
spoulson
Perl doesn't typecast at all. Any string operation, such as the concatenation operator, will turn it into a string for you.
brian d foy
Looking back, I think you're both right. I've removed it for simplicity.
spoulson
A: 

This snippet is in PHP, and it's loosely based on some example someone else had on their website somewhere (sorry buddy, I can't remember).

The basic concept is instead of using if, use a loop.

function formatNumberThousands($a,$dig)
{
    $unim = array("","k","m","g");
    $c = 0;
    while ($a>=1000 && $c<=3) {
        $c++;
        $a = $a/1000;
    }
    $d = $dig-ceil(log10($a));
    return number_format($a,($c ? $d : 0))."".$unim[$c];
}

The number_format() call is a PHP library function which returns a string with commas between the thousands groups. I'm not sure if something like it exists in perl.

The $dig parameter sets a limit on the number of digits to show. If $dig is 2, it will give you 1.2k from 1237.

To format bytes, just divide by 1024 instead.

This function is in use in some production code to this day.

Mnebuerquo
+1  A: 
sub magnitudeformat {
  my $val = shift;
  my $expstr;

  my $exp = log($val) / log(10);
     if ($exp < 3)  { return $val;   }
  elsif ($exp < 6)  { $exp = 3;  $expstr = "K"; }
  elsif ($exp < 9)  { $exp = 6;  $expstr = "M"; }
  elsif ($exp < 12) { $exp = 9;  $expstr = "G"; } # Or "B".
  else              { $exp = 12; $expstr = "T"; }

  return sprintf("%0.1f%s", $val/(10**$exp), $expstr);
}
Michael Cramer
Why stop at T? P, E, Z, Y, X, W, V, U, TD, S, R, Q, PP, O, N, MI, and L follow (though only through Y are they "official").
ysth