Inconsistency producing constant for float "infinity"

Tim Peters tim.peters at gmail.com
Fri Aug 11 14:32:26 EDT 2006


[Peter Hansen]
>> I'm investigating a puzzling problem involving an attempt to
>> generate a constant containing an (IEEE 754) "infinity" value.  (I
>> understand that special float values are a "platform-dependent
>> accident" etc...)

[also Peter]
> ...
> My guess about marshal was correct.

Yup.

> The problem (value becoming 1.0) appears when running from .pyc
> files.  Immediately after the source code is changed, the code works,
> since it doesn't unmarshal the .pyc file but just works from the
> bytecode freshly compiled in memory.
>
> This demonstrates what would be the heart of the problem, which I guess
> means this is not surprising to almost anyone, but perhaps will be a
> wakeup call to anyone who might still be unaware and has code that
> relies on constants like 1e6666 producing infinities:

It has a much better chance of working from .pyc in Python 2.5.
Michael Hudson put considerable effort into figuring out whether the
platform uses a recognizable IEEE double storage format, and, if so,
marshal and pickle take different paths that preserve infinities,
NaNs, and signed zeroes.

For Pythons earlier than that, it's a better idea to avoid trying to
express special values as literals.  That not only relies on platform
accidents about how marshal works, but also on platform accidents
about the C string->float library works.  For example, do instead:

    inf = 1e300 * 1e300
    nan = inf - inf

If you're /on/ an IEEE-754 platform, those will produce an infinity
and a NaN, from source or fom .pyc.

>  >>> import marshal
>  >>> marshal.dumps(1e666)
> 'f\x061.#INF'
>  >>> marshal.loads(marshal.dumps(1e666))
> 1.0

Illustrating the remarkable truth that the Microsoft C float->string
routines can't read what the MS string->float routines produce for
special values (try reading "1.#INF" from C code with a double format
and you'll also get 1.0 back).

Here in Python 2.5, on Windows (note that marshal grew a new binary
float format, primarily to support this):

>>> import marshal
>>> marshal.dumps(1e666)
'g\x00\x00\x00\x00\x00\x00\xf0\x7f'
>>> marshal.loads(_)
1.#INF
>>> float.__getformat__('double')   # also new in 2.5
'IEEE, little-endian'



More information about the Python-list mailing list