laziness, impatience, and hubris PerlMonks

### Integers sometimes turn into Reals after substraction

 on May 14, 2016 at 10:43 UTC Need Help??

rduke15 has asked for the wisdom of the Perl Monks concerning the following question:

Just bumped into this problem.

Some calculations seem to make a number become a "hidden" real number instead of an integer. The number prints as an integer, but after a substraction with another integer, the result is a real.

```#!/usr/bin/perl

use strict; use warnings;

my (\$x1, \$x2) = (256080, 258160);
my \$diff      = \$x2 - \$x1;

print "x1=\$x1, x2=\$x2, diff=\$diff\n"; # OK

\$x2 = 1000*240 + 1000*18 + 1000*( 4/25 );
\$diff = \$x2 - \$x1;
print "x1=\$x1, x2=\$x2, diff=\$diff\n"; # Also OK

\$x2 = 1000 * ( 4*60 + 18 + 4/25 );
\$diff = \$x2 - \$x1;
print "x1=\$x1, x2=\$x2, diff=\$diff\n"; # \$x2 is integer, but \$diff is real!
```

The output is:

```x1=256080, x2=258160, diff=2080
x1=256080, x2=258160, diff=2080
x1=256080, x2=258160, diff=2080.00000000003```

Or the short one-liner version: perl -e '\$x1=256080; \$x2 = 1000 * ( 4*60 + 18 + 4/25 ); \$diff=\$x2-\$x1; print "x1=\$x1, x2=\$x2, diff=\$diff, Perl=\$^V\n";'

I tried this on Perl versions v5.14.2, v5.18.2 and v5.20.2 with the same result. It is disconcerting that \$x2 prints as "258160", but becomes something else when used in a substraction.

Replies are listed 'Best First'.
Re: Integers sometimes turn into Reals after substraction
by LanX (Archbishop) on May 14, 2016 at 11:03 UTC
This misunderstanding is common to many languages not only Perl, because people expect a decimal calculation behind decimal representation.

But 4/25 isn't a number which can be accurately represented in a binary system and rounding errors come into play.

##### update

To elaborate further

(4*1000)/25 is an integer but 1000*(4/25) is a float with a very tiny error in the last bits of the mantissa.

Perl will still print this float like an integer because the error is too small.

But after the subtraction the leading zeros in the mantissa will cause a left shift (with adjustment of the exponent).

This means the error shifts out of Perl's tolerance margin and is printed as (decimal) fraction.

Cheers Rolf
(addicted to the Perl Programming Language and ☆☆☆☆ :)
Je suis Charlie!

Well, I understand that. The problem is that it is inconsistent. The result depends not only on the type of calculation which led to \$x2:
```perl -e '\$x1=256080; \$x2 = 1000 * ( 4*60 + 18 + 4/25 ); \$diff=\$x2-\$x1; print "x1=\$x1, x2=\$x2, diff=\$diff\n";'
x1=256080, x2=258160, diff=2080.00000000003```
```perl -e '\$x1=256080; \$x2 = 1000 * ( 4*60 + 18 ) + 1000*(4/25); \$diff=\$x2-\$x1; print "x1=\$x1, x2=\$x2, diff=\$diff\n";'
x1=256080, x2=258160, diff=2080```

but also on the value of \$x1 which has never been calculated.

```perl -e '\$x1=25608; \$x2 = 1000 * ( 4*60 + 18 + 4/25 ); \$diff=\$x2-\$x1; print "x1=\$x1, x2=\$x2, diff=\$diff\n";'
x1=25608, x2=258160, diff=232552```
Have you seen the update in my reply?

##### update

Think float, this

```x1   = 256080
x2   = 258160
diff = 2080.00000000003

really just means (in slightly inaccurate decimal interpretation)

```x1   = 2.56080            e5
x2   = 2.5816000000000003 e5  (error ignored when printed)
diff = 2.08000000000003   e3  (error visible when printed)

As you can see the error (here decimal 3) is shifted to the left and out of error margin into printed "visibility".

##### update

after firing up my laptop, here a proof of concept

```  DB<115> \$x1 = 2.56080e5
=> 256080

DB<116> \$x2 = 2.5816000000000003e5
=> 258160                 # within error margin, ignored in normal di
+splay

DB<117> printf '%.11f', \$x2
258160.00000000003         # forced into visibility, hence \$x2 NOT an
+integer

DB<118> \$x2-\$x1
=> 2080.00000000003       # error can't be ignored any more

Cheers Rolf
(addicted to the Perl Programming Language and ☆☆☆☆ :)
Je suis Charlie!

Re: Integers sometimes turn into Reals after substraction
by ww (Archbishop) on May 14, 2016 at 11:45 UTC
Re: Integers sometimes turn into Reals after substraction
by syphilis (Bishop) on May 14, 2016 at 14:03 UTC
It is disconcerting that \$x2 prints as "258160", but becomes something else when used in a subtraction

I totally agree with you.
The internal hex representation of the double 258160.0 is 410f838000000000, but the internal hex representation of \$x2 is 410f838000000001 which should really be printed as 2.5816000000000003e5.
Unfortunately, perl limits the printed output of floats to 15 decimal digits - and, as you can see, if you round \$x2 to 15 decimal digits of precision you get 258160.0.

There has been some talk about fixing this in perl-5.26.

Cheers,
Rob
Re: Integers sometimes turn into Reals after substraction
by ikegami (Pope) on May 14, 2016 at 15:23 UTC

First of all, 4/25 is periodic in binary (just like 1/3 is periodic in decimal).

```         ____________________
4/25 = 0.00101000111101011100 base 2

This means it can't be stored exactly in a floating point number.

Sometimes, floating point number errors get canceled out or reduced to nothing. It turns out that this happens for one of the chains of operations but not the other.

That's why you want to avoid checking if two floating point numbers are equal without allowing for an error margin. Replace

```\$n1 == \$n2

with

```abs(\$n1 - \$n2) < \$epsilon
Well Perl's error correction (or that of the underlying C lib) is better than it's reputation
```
DB<186> \$x1= 1000*(4/25)
=> 160

DB<187> \$x2= 4000/25
=> 160

DB<188> \$x1 == \$x2
=> 1

What's puzzling me is this behaviour:

```  DB<205>  for (254..263) {\$x = 1000 * ( \$_ + 4/25 ); printf "%.20f\n"
+, \$x}

254160.00000000000000000000
255160.00000000000000000000
256160.00000000002910383046
257160.00000000002910383046
258160.00000000002910383046
259160.00000000002910383046
260160.00000000002910383046
261160.00000000002910383046
262160.00000000000000000000
263160.00000000000000000000

Cheers Rolf
(addicted to the Perl Programming Language and ☆☆☆☆ :)
Je suis Charlie!

##### update

interestingly this only seems to happen near some powers of 2

```  DB<220>  for (0..20) {\$e=2**\$_; \$x = 1000 * ( \$e + 4/25 ); printf "\$
+_:\$e => %.20f\n", \$x if (\$x-int(\$x))}
5:32 => 32159.99999999999636202119
8:256 => 256160.00000000002910383046
9:512 => 512159.99999999994179233909
10:1024 => 1024160.00000000011641532183
11:2048 => 2048159.99999999976716935635
15:32768 => 32768160.00000000372529029846
18:262144 => 262144159.99999997019767761230
19:524288 => 524288160.00000005960464477539
20:1048576 => 1048576159.99999988079071044922

##### update

in hindsight this may be an effect of precision and correction of the underlying processor and usage of arithmetic units, hence machine dependent.

IEEE double-precision float is 53 bit float. 0.00000000002910383046 = 2**-35. With the 18 bits for the integral portion of 255160 or 256160, that's the full 53bits. So, apparently, when you take the 53bit fractional representation of 255.16 multiplied by 1000dec, round-off sets the final bit to 0; when you take 256.16 * 1000, round-off sets the final bit to 1. Now (when I have time) I'm going to have to figure out the formula to figure out which powers of two will have this error and which won't. It all depends on the 53rd bit of the product of 2**N+16/25 times 128 (same as the bits of times 1000, since the factor of 8 is a binary shift). Oh, I'm nearly there... the 125 is 0b111_1101... and that will just interact with the 53rd bit... urgh but I've got to leave now... I guess it's an exercise for later. :-)

Re: Integers sometimes turn into Reals after substraction
by Laurent_R (Canon) on May 14, 2016 at 15:50 UTC
Running your code under Perl 6 displays the following output for the last line:
```x1=256080, x2=258160, diff=2080
And you can even compare \$diff with 2080:
```say \$diff == 2080;   # -> True
The reason for the difference is that Perl 6 is not using floats for such calculations, but rationals, so that 4/25 is not stored internally as 0.16, but as 4/25, i.e. a numerator and a denominator.

Create A New User
Node Status?
node history
Node Type: perlquestion [id://1163025]
Approved by ww
Front-paged by 1nickt
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (5)
As of 2020-04-03 20:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
The most amusing oxymoron is:

Results (32 votes). Check out past polls.

Notices?