How do you round a floating point number in Perl?

Go To StackoverFlow.com

164

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

e.g.

1.2 = 1
1.7 = 2
2008-10-07 13:52
by Ranguard


189

Output of perldoc -q round

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

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.

2008-10-07 13:54
by Vinko Vrsalovic
^ Thariama, why would ceil be deprecated? It's not deprecated in POSIX or perl as far as I know. Citation needed - Sam Watkins 2013-10-31 06:42
@Beginners, don't try to use printf if you want the result in a variable, use sprintf... hope this saves you some debugging-time :- - Boris Däppen 2016-03-16 20:29
Can I use int() on PDLs - CinCout 2017-06-20 11:13
use POSIX;
$x = ($x - floor($x) >= .5) ? ceil($x) : floor($x) - Joseph Argenio 2017-10-08 18:49


127

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.

2009-08-13 21:26
by RET
... but it fails on negative numbers: still better sprint - alessandro 2012-10-29 09:58
Ah no, it does not. Rounding up a negative number takes you closer to zero, not further away. What are they teaching in schools these days - RET 2012-10-29 22:05
@RET Yes, it does fail with negative numbers. $float=-1.4 results in 0 with this method. That is not what they taught at my school. Remember that int() truncates towards zero - fishinear 2012-12-20 09:36
@fishinear You are correct, and I am duly chastened. But I did say 'for trivial use-case'. My answer has been corrected - RET 2012-12-20 23:34
For instances where I was just correcting for floating points incapable of reaching an integer, I used this modified solution: my $rounded = floor($float + 0.1) - HoldOffHunger 2016-09-23 19:38
Note that it $float = 0, this will fail :- - mat 2017-03-15 17:24
@mat That's a correction that's been a long time coming! Duly fixed, thanks for pointing it out - RET 2017-03-15 19:41


71

You can either use a module like Math::Round:

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

Or you can do it the crude way:

my $rounded = sprintf "%.0f", $float;
2008-10-07 13:54
by EvdB


45

If you decide to use printf or sprintf, note that they use the 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
2008-10-07 15:37
by Kyle
Thanks for pointing this out.

More precisely, the name of the method is 'Round Half to Even' - Jean Vincent 2010-09-12 09:51

All the answers that mention printf, or sprintf should mention this - insaner 2016-02-05 07:56
This is an extremly important information. I had serveral times bugs in software because I assumed 5 will always be rounded up. I finally found, why perl never did what I wanted. Thanks for pointing this out - Boris Däppen 2017-07-11 13:45
Actually, this is OS dependent! In Windows it will round half away from zero and unix-like will round half to even: http://www.exploringbinary.com/inconsistent-rounding-of-printed-floating-point-numbers - Apoc 2017-07-24 15:57


8

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.

2008-10-07 13:58
by Kent Fredric


3

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.

2011-06-13 20:51
by activealexaoki
Once again, why answer an ancient question with a complicated answer when something like RET's answer works equally well - Joel Berger 2011-06-13 21:51
This really isn't very complicated, and RET's answer involves a bunch of math that a) theoretically risks overflow, b) takes longer, and c) needlessly introduces more fp imprecision to your final value. Wait, which one is complicated again? ; - cptstubing06 2013-05-30 21:51


1

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.

2010-12-21 21:06
by David Beckman


1

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;
2011-08-22 22:38
by matt


1

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);
}
2014-09-12 14:13
by seacoder


0

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 );
2011-11-09 11:35
by Akvel


0

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); 
2016-09-23 19:47
by HoldOffHunger


-2

cat table |
  perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";' 
2009-08-13 14:25
by NoName
Ads