2

With this line, I expect the output 201

$ perl -e '$e = '2.01'; $c = sprintf("%d", $e * 100); print $c;'

But I get 200 instead. I don't understand why.

nowox
  • 25,978
  • 39
  • 143
  • 293

4 Answers4

3

Because of the binary representation of a float number.

Try:

perl -e '$e = 2.01;printf("%2.25f\n",$e);'

Output:

2.0099999999999997868371793

The intetger part of this number multiplied by 100 gives 200

Toto
  • 89,455
  • 62
  • 89
  • 125
2
perl -e 'my $e = "2.01"; my $c = sprintf("%.0f", $e * 100); print $c;'

With %d, the integer is truncated.

And if you don't bother with printf :

perl -e 'my $e = "2.01"; my $c = $e * 100; print $c;'
Gilles Quénot
  • 173,512
  • 41
  • 224
  • 223
  • I agree with your but it should also work with `%d`, right? 201 is still an integer... – nowox Dec 09 '14 at 14:50
  • 1
    @coin But `2.01` is not. And internally it might not be saved as exactly that value but a tiny bit more or less. And then truncating it may get you a wrong result. As a rule of thumb, never interpret floats as integers without actually rounding. – DeVadder Dec 09 '14 at 15:06
  • @DeVadder So what's the proper way to get `201` from `'2.01'` ? – nowox Dec 09 '14 at 15:09
  • @coin http://stackoverflow.com/questions/178539/how-do-you-round-a-floating-point-number-in-perl has you covered. The easiest way to round a *POSITIVE* float into an integer is probably `my $int = int($float + 0.5);` Obviously that may become a problem if behavior for values near to .5 are important for you and *DOES NOT WORK* for negative values (because `int` always truncates to zero). As such using it is certainly a bad habit. – DeVadder Dec 09 '14 at 15:18
  • 2
    @sputnick: `%d` *doesn't round the integer: that is the whole problem. If it did, the output would be `201`. It *truncates* it towards zero. – Borodin Dec 09 '14 at 15:46
1

Floating point representation has a limit to its accuracy, and the binary representation of 2.01 just happens to be fractionally less than 2.01.

The %d format conversion truncates the value to the next lowest value. It actually does a call to int, whose documentation says

You should not use this function for rounding: one because it truncates towards 0 , and two because machine representations of floating-point numbers can sometimes produce counterintuitive results.

And int(200.99999999999) is 200, which is what you are getting.

The canonical way in Perl to get the closest integer to a floating point value is to use the %f conversion with zero decimal points. So if you write instead

perl -e '$e = '2.01'; $c = sprintf("%.0f", $e * 100); print $c;'

you will get the output

200
Borodin
  • 126,100
  • 9
  • 70
  • 144
0

Following document mentions about floating point and binary representation. This is from python.org, but it also works for perl.

https://docs.python.org/3/tutorial/floatingpoint.html

$ perl -e 'if (2.01 + 2.01 + 2.01 == 6.03) { print "yes" } else { print "no" }'
no

You can try using bignum pragma as a makeshift:

$ perl -Mbignum -e '$e = "2.01"; $c = sprintf("%d", $e * 100); print $c;'
201
ernix
  • 3,442
  • 1
  • 17
  • 23