4

Knowing about inaccuracies because of internal representation of floating point numbers (see Wikipedia, search down to: "The use of the equality test"...), there's still a surprise when doing:

% expr int(0.57 * 10000)
5699
% expr int([expr 0.57 * 10000])
5700

Are nested expr forbidden? Why does it alter the floating point value that's passed through? Or does it change the order in which the floating point arithmetic is done, those influencing the result?

Update: Good reading on the topic of comparing floating point numbers with speed (and the secondary links), some basics and not so basics here, and in the IEEE 754-2008 standard's description.

Community
  • 1
  • 1
cfi
  • 10,915
  • 8
  • 57
  • 103

1 Answers1

7

This is an interesting issue, since it touches on a few subtle things.

% expr int(0.57 * 10000)
5699

This code (with no substitutions, so it works “unsurprisingly”) shows that floating point numbers are surprising things in themselves. In particular, 0.57 doesn't have an exact representation as an IEEE double precision floating point number (which is base-2); in fact, it's representation is a little bit lower than exactly 0.57 and so when rounded down (which is what int(...) does; the 10000 itself is exact) you go down to 5699. You'll see the same behavior with other languages too.

% expr int([expr 0.57 * 10000])
5700

Now this is particularly interesting. You're seeing first the inner computation being done and the resulting float converted to a string (because there's really no other way to do it). Now, you must be using Tcl 8.4 (or before) where the default number rendering rules were (in effect) what you'd get by printing the first 15 significant digits of the number; in this case that gives you 5700.00000000000 (well, with some truncation of zeroes on the right) and that's then reinterpreted from scratch as first a double (exactly 5700.0) and that's then converted to 5700.

The rules for number conversion changed in Tcl 8.5. Nowadays, when Tcl converts floating point numbers to strings, it produces the shortest string which will convert back to exactly the same floating point number (i.e., a side trip through the land of strings will give the same bit pattern in the resulting double). This means in turn that you now never see a difference between the two things above. (If you really want to force a fixed number of decimal places, use format %.15g [expr 0.57 * 10000].)

You also won't the observed behavior with 8.0 to 8.4 if you brace your expressions properly, as the community has advised people to do for well over a decade:

$ tclsh8.4
% expr {int([expr 0.57 * 10000])}
5699

That's because this doesn't force the result of the internal expr call to be interpreted as a string. (It's also faster and safer.)

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • Thanks for confirming a suspicion and adding a lot of knowledge. And, yes, if there were a tcl8.4 tag I would have set it, Sherlock ;-) – cfi Mar 28 '12 at 10:57