How do you round a floating point number in Perl?

PerlFloating PointRounding

Perl Problem Overview


How can I round a decimal number (floating point) to the nearest integer?

e.g.

1.2 = 1
1.7 = 2

Perl Solutions


Solution 1 - Perl

Output of [perldoc -q round][round]

Does Perl have a round() function? What about ceil() and floor()? Trig functions?

Remember that http://perldoc.perl.org/functions/int.html"><code>int()</code></a> merely truncates toward 0. For rounding to a certain number of digits, http://perldoc.perl.org/functions/sprintf.html"><code>sprintf()</code></a> or http://perldoc.perl.org/functions/printf.html"><code>printf()</code></a> is usually the easiest route.

    printf("%.3f", 3.1415926535);       # prints 3.142

The http://perldoc.perl.org/POSIX.html"><code>POSIX</code></a> module (part of the standard Perl distribution) implements ceil(), floor(), and a number of other mathematical and trigonometric functions.

    use POSIX;
$ceil   = ceil(3.5);                        # 4
$floor  = floor(3.5);                       # 3

In 5.000 to 5.003 perls, trigonometry was done in the http://perldoc.perl.org/Math/Complex.html"><code>Math::Complex</code></a> module. With 5.004, the http://perldoc.perl.org/Math/Trig.html"><code>Math::Trig</code></a> module (part of the standard Perl distribution) implements the trigonometric functions. Internally it uses the http://perldoc.perl.org/Math/Complex.html"><code>Math::Complex</code></a> module and some functions can break out from the real axis into the complex plane, for example the inverse sine of 2.

Rounding in financial applications can have serious implications, and the rounding method used should be specified precisely. In these cases, it probably pays not to trust whichever system rounding is being used by Perl, but to instead implement the rounding function you need yourself.

To see why, notice how you'll still have an issue on half-way-point alternation:

    for ($i = 0; $i < 1.01; $i += 0.05) { printf "%.1f ",$i}



0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7
0.8 0.8 0.9 0.9 1.0 1.0


Don't blame Perl. It's the same as in C. IEEE says we have to do this. Perl numbers whose absolute values are integers under 2**31 (on 32 bit machines) will work pretty much like mathematical integers. Other numbers are not guaranteed.

[round]: http://perldoc.perl.org/perlfaq4.html#Does-Perl-have-a-round%28)-function%3f--What-about-ceil()-and-floor()%3f--Trig-functions%3f [sprintf]: http://perldoc.perl.org/functions/sprintf.html [printf]: http://perldoc.perl.org/functions/printf.html [int]: http://perldoc.perl.org/functions/int.html [Math::Trig]: http://perldoc.perl.org/Math/Trig.html [POSIX]: http://perldoc.perl.org/POSIX.html [Math::Complex]: http://perldoc.perl.org/Math/Complex.html

Solution 2 - Perl

Whilst not disagreeing with the complex answers about half-way marks and so on, for the more common (and possibly trivial) use-case:

my $rounded = int($float + 0.5);

UPDATE

If it's possible for your $float to be negative, the following variation will produce the correct result:

my $rounded = int($float + $float/abs($float*2 || 1));

With this calculation -1.4 is rounded to -1, and -1.6 to -2, and zero won't explode.

Solution 3 - Perl

You can either use a module like http://search.cpan.org/dist/Math-Round">Math::Round</a>;:

use Math::Round;
my $rounded = round( $float );

Or you can do it the crude way:

my $rounded = sprintf "%.0f", $float;

Solution 4 - Perl

If you decide to use printf or sprintf, note that they use the http://en.wikipedia.org/wiki/Rounding#Round_half_to_even">Round half to even method.

foreach my $i ( 0.5, 1.5, 2.5, 3.5 ) {
    printf "$i -> %.0f\n", $i;
}
__END__
0.5 -> 0
1.5 -> 2
2.5 -> 2
3.5 -> 4

Solution 5 - Perl

See perldoc/perlfaq:

> Remember that int() merely truncates toward 0. For rounding to a > certain number of digits, sprintf() or printf() is usually the > easiest route. > > printf("%.3f",3.1415926535); > # prints 3.142 > > The POSIX module (part of the standard Perl distribution) > implements ceil(), floor(), and a number of other mathematical > and trigonometric functions. > > use POSIX; > $ceil = ceil(3.5); # 4 > $floor = floor(3.5); # 3 > > > In 5.000 to 5.003 perls, trigonometry was done in the Math::Complex module. > > With 5.004, the Math::Trig module (part of the standard Perl distribution) > implements the trigonometric functions. > > Internally it uses the Math::Complex module and some functions can break > out from the real axis into the complex plane, for example the inverse sine of 2. > > Rounding in financial applications can have serious implications, and the rounding > method used should be specified precisely. In these cases, it probably pays not to > trust whichever system rounding is being used by Perl, but to instead implement the > rounding function you need yourself. > > To see why, notice how you'll still have an issue on half-way-point alternation: > > for ($i = 0; $i < 1.01; $i += 0.05) > { > printf "%.1f ",$i > } > > 0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7 0.8 0.8 0.9 0.9 1.0 1.0 > > > Don't blame Perl. It's the same as in C. IEEE says we have to do > this. Perl numbers whose absolute values are integers under 2**31 (on > 32 bit machines) will work pretty much like mathematical integers. > Other numbers are not guaranteed.

Solution 6 - Perl

You don't need any external module.

$x[0] = 1.2;
$x[1] = 1.7;

foreach (@x){
  print $_.' = '.( ( ($_-int($_))<0.5) ? int($_) : int($_)+1 );
  print "\n";
}

I may be missing your point, but I thought this was much cleaner way to do the same job.

What this does is to walk through every positive number in the element, print the number and rounded integer in the format you mentioned. The code concatenates respective rounded positive integer only based on the decimals. int($) basically round-down the number so ($-int($_)) captures the decimals. If the decimals are (by definition) strictly less than 0.5, round-down the number. If not, round-up by adding 1.

Solution 7 - Perl

The following will round positive or negative numbers to a given decimal position:

sub round ()
{
    my ($x, $pow10) = @_;
    my $a = 10 ** $pow10;

    return (int($x / $a + (($x < 0) ? -0.5 : 0.5)) * $a);
}

Solution 8 - Perl

Following is a sample of five different ways to summate values. The first is a naive way to perform the summation (and fails). The second attempts to use sprintf(), but it too fails. The third uses sprintf() successfully while the final two (4th & 5th) use floor($value + 0.5).

 use strict;
 use warnings;
 use POSIX;

 my @values = (26.67,62.51,62.51,62.51,68.82,79.39,79.39);
 my $total1 = 0.00;
 my $total2 = 0;
 my $total3 = 0;
 my $total4 = 0.00;
 my $total5 = 0;
 my $value1;
 my $value2;
 my $value3;
 my $value4;
 my $value5;

 foreach $value1 (@values)
 {
      $value2 = $value1;
      $value3 = $value1;
      $value4 = $value1;
      $value5 = $value1;

      $total1 += $value1;

      $total2 += sprintf('%d', $value2 * 100);

      $value3 = sprintf('%1.2f', $value3);
      $value3 =~ s/\.//;
      $total3 += $value3;

      $total4 += $value4;

      $total5 += floor(($value5 * 100.0) + 0.5);
 }

 $total1 *= 100;
 $total4 = floor(($total4 * 100.0) + 0.5);

 print '$total1: '.sprintf('%011d', $total1)."\n";
 print '$total2: '.sprintf('%011d', $total2)."\n";
 print '$total3: '.sprintf('%011d', $total3)."\n";
 print '$total4: '.sprintf('%011d', $total4)."\n";
 print '$total5: '.sprintf('%011d', $total5)."\n";

 exit(0);

 #$total1: 00000044179
 #$total2: 00000044179
 #$total3: 00000044180
 #$total4: 00000044180
 #$total5: 00000044180

Note that floor($value + 0.5) can be replaced with int($value + 0.5) to remove the dependency on POSIX.

Solution 9 - Perl

Negative numbers can add some quirks that people need to be aware of.

printf-style approaches give us correct numbers, but they can result in some odd displays. We have discovered that this method (in my opinion, stupidly) puts in a - sign whether or not it should or shouldn't. For example, -0.01 rounded to one decimal place returns a -0.0, rather than just 0. If you are going to do the printf style approach, and you know you want no decimal, use %d and not %f (when you need decimals, it's when the display gets wonky).

While it's correct and for math no big deal, for display it just looks weird showing something like "-0.0".

For the int method, negative numbers can change what you want as a result (though there are some arguments that can be made they are correct).

The int + 0.5 causes real issues with -negative numbers, unless you want it to work that way, but I imagine most people don't. -0.9 should probably round to -1, not 0. If you know that you want negative to be a ceiling rather than a floor then you can do it in one-liner, otherwise, you might want to use the int method with a minor modification (this obviously only works to get back whole numbers:

my $var = -9.1;
my $tmpRounded = int( abs($var) + 0.5));
my $finalRounded = $var >= 0 ? 0 + $tmpRounded : 0 - $tmpRounded;

Solution 10 - Perl

My solution for sprintf

if ($value =~ m/\d\..*5$/){
	$format =~ /.*(\d)f$/;
	if (defined $1){
	   my $coef = "0." . "0" x $1 . "05";	 
        	$value = $value + $coef;	
	}
}

$value = sprintf( "$format", $value );

Solution 11 - Perl

If you are only concerned with getting an integer value out of a whole floating point number (i.e. 12347.9999 or 54321.0001), this approach (borrowed and modified from above) will do the trick:

my $rounded = floor($float + 0.1); 

Solution 12 - Perl

Using Math::BigFloat you can do something like this:

use Math::BigFloat;

print Math::BigFloat->new(1.2)->bfround(1); ## 1
print Math::BigFloat->new(1.7)->bfround(1); ## 2

This can be wrapped in a subroutine

use Math::BigFloat;

sub round {
  Math::BigFloat->new(shift)->bfround(1);
}

print round(1.2); ## 1
print round(1.7); ## 2

Solution 13 - Perl

loads of reading documentation on how to round numbers, many experts suggest writing your own rounding routines, as the 'canned' version provided with your language may not be precise enough, or contain errors. i imagine, however, they're talking many decimal places not just one, two, or three. with that in mind, here is my solution (although not EXACTLY as requested as my needs are to display dollars - the process is not much different, though).

sub asDollars($) {
  my ($cost) = @_;
  my $rv = 0;

  my $negative = 0;
  if ($cost =~ /^-/) {
    $negative = 1;
    $cost =~ s/^-//;
  }

  my @cost = split(/\./, $cost);

  # let's get the first 3 digits of $cost[1]
  my ($digit1, $digit2, $digit3) = split("", $cost[1]);
  # now, is $digit3 >= 5?
  # if yes, plus one to $digit2.
  # is $digit2 > 9 now?
  # if yes, $digit2 = 0, $digit1++
  # is $digit1 > 9 now??
  # if yes, $digit1 = 0, $cost[0]++
  if ($digit3 >= 5) {
    $digit3 = 0;
    $digit2++;
    if ($digit2 > 9) {
      $digit2 = 0;
      $digit1++;
      if ($digit1 > 9) {
        $digit1 = 0;
        $cost[0]++;
      }
    }
  }
  $cost[1] = $digit1 . $digit2;
  if ($digit1 ne "0" and $cost[1] < 10) { $cost[1] .= "0"; }

  # and pretty up the left of decimal
  if ($cost[0] > 999) { $cost[0] = commafied($cost[0]); }

  $rv = join(".", @cost);

  if ($negative) { $rv = "-" . $rv; }

  return $rv;
}

sub commafied($) {
  #*
  # to insert commas before every 3rd number (from the right)
  # positive or negative numbers
  #*
  my ($num) = @_; # the number to insert commas into!

  my $negative = 0;
  if ($num =~ /^-/) {
    $negative = 1;
    $num =~ s/^-//;
  }
  $num =~ s/^(0)*//; # strip LEADING zeros from given number!
  $num =~ s/0/-/g; # convert zeros to dashes because ... computers!

  if ($num) {
    my @digits = reverse split("", $num);
    $num = "";

    for (my $i = 0; $i < @digits; $i += 3) {
      $num .= $digits[$i];
      if ($digits[$i+1]) { $num .= $digits[$i+1]; }
      if ($digits[$i+2]) { $num .= $digits[$i+2]; }
      if ($i < (@digits - 3)) { $num .= ","; }
      if ($i >= @digits) { last; }
    }

    #$num =~ s/,$//;
    $num = join("", reverse split("", $num));
    $num =~ s/-/0/g;
  }

  if ($negative) { $num = "-" . $num; }

  return $num; # a number with commas added
  #usage: my $prettyNum = commafied(1234567890);
}

Solution 14 - Perl

cat table |
  perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";' 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRanguardView Question on Stackoverflow
Solution 1 - PerlVinko VrsalovicView Answer on Stackoverflow
Solution 2 - PerlRETView Answer on Stackoverflow
Solution 3 - PerlEvdBView Answer on Stackoverflow
Solution 4 - PerlKyleView Answer on Stackoverflow
Solution 5 - PerlKent FredricView Answer on Stackoverflow
Solution 6 - PerlactivealexaokiView Answer on Stackoverflow
Solution 7 - PerlseacoderView Answer on Stackoverflow
Solution 8 - PerlDavid BeckmanView Answer on Stackoverflow
Solution 9 - PerlmattView Answer on Stackoverflow
Solution 10 - PerlAkvelView Answer on Stackoverflow
Solution 11 - PerlHoldOffHungerView Answer on Stackoverflow
Solution 12 - PerlA1rPunView Answer on Stackoverflow
Solution 13 - PerlJarett LloydView Answer on Stackoverflow
Solution 14 - Perluser155846View Answer on Stackoverflow