in reply to Re^4: Integers sometimes turn into Reals after substraction (printed accuracy and float subtraction)
in thread Integers sometimes turn into Reals after substraction

The order of operations is undefined when there is no sequence point, and there is no inherent difference in the precedence of the operators in the expression. At least if it had been 1000+4/25, it would be pretty obvious that the 4/25 has to be evaluated before 1000 can be added to it. In a language like C or C++ we would say it's up to the implementation. It's not so different with Perl, in this example, except that the implementation is a little more like a functioning language specification on matters that aren't explicit in documentation or tests.

With C or C++ (or other typed languages) it would be reasonably trivial to declare all the types in question to be int's and then there would be no risk of the unexpected, unless you expect int(4/25) to be a non-zero value.

If you want to exercise a little more caution you could re-order the operations by using explicit precedence; (1000*4)/25 ought to get you a pretty sensible result.

There is an article that becomes a bit of a dense read, but it otherwise quite good, and will serve to shed light on the issue: What Every Computer Scientist Should Know About Floating Point Math. But if that's a little too much to get through, consider this:

We grow up and in elementary school are taught that 1/3rd is just about 0.33. We know that it isn't exactly 0.33, and in fact we know that no matter how many 3's we add to the end of the number we'll still never quite get to 1.3rd in our decimal representation. And yet when we see 33%, we naturally conclude that we're talking about 1/3rd of some entity. It comes pretty natural to us, and we accept it. ...mostly because it's taught from an early age, and because with our ten fingers we tend to think in terms of decimal.

As you're certainly aware computers don't think in decimal. The fraction 1/3rd is impossible for us to represent in decimal. But in binary, the fraction 1/10th is impossible to represent accurately. In fact, any number that cannot be represented evenly by n/(2^m) becomes as problematic for a computer to represent as 1/3rd becomes for us with our decimal system. 4/25th's could be 8/50's, or 16/100ths, 32/200ths, 64/400ths, 128/800's, 256/1600ths. All very easy to look at in fractional form, all a perfectly clean 0.16 in base 10, but in its binary representation (converted back to base-10), about the closest our computers can come is 1600000000000000033306690738754696212708950042724609375. Even if you remove division from it, and try the following:

$ perl -E 'printf "%.56f\n", .16'

You will see something like...

0.16000000000000000333066907387546962127089500427246093750

There are various strategies for overcoming this limitation in how computers represent floating point internally. One is to do all your math at a multiple of 100 or 1000, and then round results to integers. Another might be to simply forge ahead with floating point but sprintf "%.0f" with the result before outputting it. Or store all your floats as fractions (keep track of the numerator and denominator). Whatever strategy you need, just be consistent within your application.


Dave

  • Comment on Re^5: Integers sometimes turn into Reals after substraction (printed accuracy and float subtraction)
  • Select or Download Code