[Python-Dev] Round Bug in Python 1.6?
Tim Peters
tim_one@email.msn.com
Fri, 7 Apr 2000 21:41:48 -0400
[Ka-Ping Yee]
> ,,,
> Now you say that 17 significant digits are required to ensure
> that eval(repr(x)) == x,
Yes. This was first proved in Jerome Coonen's doctoral dissertation, and is
one of the few things IEEE-754 guarantees about fp I/O: that
input(output(x)) == x for all finite double x provided that output()
produces at least 17 significant decimal digits (and 17 is minimal). In
particular, IEEE-754 does *not* guarantee that either I or O are properly
rounded, which latter is needed for what *you* want to see here. The std
doesn't require proper rounding in this case (despite that it requires it in
all other cases) because no efficient method for doing properly rounded I/O
was known at the time (and, alas, that's still true).
> but we surely know that 17 digits are *not* required when x is A
> because i *just typed in* 3.1416 and the best choice of double value
> was A.
Well, x = 1.0 provides a simpler case <wink>.
> I haven't gone and figured it out, but i'll take your word for
> it that 17 digits may be required in *certain* cases to ensure
> that eval(repr(x)) == x. They're just not required in all cases.
>
> It's very jarring to type something in, and have the interpreter
> give you back something that looks very different.
It's in the very nature of binary floating-point that the numbers they type
in are often not the numbers the system uses.
> It breaks a fundamental rule of consistency, and that damages the user's
> trust in the system or their understanding of the system.
If they're surprised by this, they indeed don't understand the arithmetic at
all! This is an argument for using a different form of arithmetic, not for
lying about reality.
> (What do you do then, start explaining the IEEE double representation
> to your CP4E beginner?)
As above. repr() shouldn't be used at the interactive prompt anyway (but
note that I did not say str() should be).
> What should really happen is that floats intelligently print in
> the shortest and simplest manner possible, i.e. the fewest
> number of digits such that the decimal representation will
> convert back to the actual value. Now you may say this is a
> pain to implement, but i'm talking about sanity for the user here.
This can be done, but only if Python does all fp I/O conversions entirely on
its own -- 754-conforming libc routines are inadequate for this purpose
(and, indeed, I don't believe any libc other than Sun's does do proper
rounding here).
For background and code, track down "How To Print Floating-Point Numbers
Accurately" by Steele & White, and its companion paper (s/Print/Read/) by
Clinger. Steele & White were specifically concerned with printing the
"shortest" fp representation possible such that proper input could later
reconstruct the value exactly. Steele, White & Clinger give relatively
simple code for this that relies on unbounded int arithmetic.
Excruciatingly difficult and platform-#ifdef'ed "optimized" code for this
was written & refined over several years by the numerical analyst David Gay,
and is available from Netlib.
> I haven't investigated how to do this best yet. I'll go off
> now and see if i can come up with an algorithm that's not
> quite so stupid as
>
> def smartrepr(x):
> p = 17
> while eval('%%.%df' % (p - 1) % x) == x: p = p - 1
> return '%%.%df' % p % x
This merely exposes accidents in the libc on the specific platform you run
it. That is, after
print smartrepr(x)
on IEEE-754 platform A, reading that back in on IEEE-754 platform B may not
yield the same number platform A started with. Both platforms have to do
proper rounding to make this work; there's no way to do proper rounding by
using libc; so Python has to do it itself; there's no efficient way to do it
regardless; nevertheless, it's a noble goal, and at least a few languages in
the Lisp family require it (most notably Scheme, from whence Steele, White &
Clinger's interest in the subject).
you're-in-over-your-head-before-the-water-touches-your-toes<wink>-ly
y'rs - tim