473,396 Members | 1,892 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Inconsistency producing constant for float "infinity"

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...)

The issue appears possibly to point to a bug in the Python compiler,
with it producing inconsistent results. I'm using "Python 2.4.2 (#67,
Sep 28 2005, 12:41:11) [MSC v.1310 32 bit (Intel)] on win32".

This code sometimes produces a float of 1.0, sometimes infinity (or,
since I'm on Windows, the float with string representation of "1.#INF"),
as seen in the operand of the LOAD_CONST instruction:

def floatstr(o, allow_nan=True):
INFINITY = 1e66666

# more code follows which does *not* rebind INFINITY

>>import dis
dis.dis(floatstr)
27 0 LOAD_CONST 1 (1.0)
3 STORE_FAST 2 (INFINITY)
....
And, at other times, under circumstances I've yet to isolate:
>>import dis
dis.dis(floatstr)
27 0 LOAD_CONST 1 (1.#INF)
3 STORE_FAST 2 (INFINITY)
...

I'll keep digging to narrow down what's going on, but I wondered if
anyone had heard of or seen a similar problem, or is aware of an
existing issue that could cause this. I checked the list on sourceforge
but can't see anything relevant, nor did Google help.

Just had a thought: could this be an issue involving "marshal" and
either the writing of or reading of the .pyc file? Is it known to be
unsafe to have Python source with special float constants?

-Peter

Aug 11 '06 #1
5 4156
Sybren Stuvel wrote:
Peter Hansen enlightened us with:
>>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...)

Why aren't you simply using the fpconst package?
Probably because it's not in the stdlib yet, assuming that's still true.

(Using it might be an option anyway. I'm investigating a problem on
Win32 with simplejson, so it would be Bob Ippolito's choice whether
fpconst is a reasonable solution to the problem.)

My guess about marshal was correct. 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:
>>import marshal
marshal.dumps(1e666)
'f\x061.#INF'
>>marshal.loads(marshal.dumps(1e666))
1.0

-Peter

Aug 11 '06 #2
[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'
Aug 11 '06 #3
Tim Peters <ti********@gmail.comwrote:
...
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.
Isn't marshal constrained to work across platforms (for a given Python
release), and pickle also constrainted to work across releases (for a
given protocol)? I'm curious about how this still allows them to "take
different paths" (yeah, I _could_ study the sources, but I'm lazy:-)...
Alex
Aug 12 '06 #4
[Tim Peters]
...
>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.
[Alex Martelli]
Isn't marshal constrained to work across platforms (for a given Python
release), and pickle also constrainted to work across releases (for a
given protocol)?
Yes to both.
I'm curious about how this still allows them to "take different
paths" (yeah, I _could_ study the sources, but I'm lazy:-)...
Good questions. Pickle first: pickle, with protocol >= 1, has always
had a binary format for Python floats, which is identical to the
big-endian IEEE-754 double-precision storage format. This is
independent of the native C double representation: even on a non-IEEE
box (e.g, VAX or Cray), protocol >= 1 pickle does the best it can to
/encode/ native doubles in the big-endian 754 double storage /format/.
This is explained in the docs for the "float8" opcode in
pickletools.py:

The format is unique to Python, and shared with the struct
module (format string '>d') "in theory" (the struct and cPickle
implementations don't share the code -- they should). It's
strongly related to the IEEE-754 double format, and, in normal
cases, is in fact identical to the big-endian 754 double format.
On other boxes the dynamic range is limited to that of a 754
double, and "add a half and chop" rounding is used to reduce
the precision to 53 bits. However, even on a 754 box,
infinities, NaNs, and minus zero may not be handled correctly
(may not survive roundtrip pickling intact).

The problem has been that C89 defines nothing about signed zeroes,
infinities, or NaNs, so even on a 754 box there was no consistency
across platforms in what C library routines like frexp() returned when
fed one of those things. As a result, what Python's "best non-heroic
effort" code for constructing a 754 big-endian representation actually
did was a platform-dependent accident when fed a 754 special-case
value. Likewise for trying to construct a native C double from a 754
representation of a 754 special-case value -- again, there was no
guessing what C library routines like ldexp() would return in those
cases.

Part of what Michael Hudson did for 2.5 is add code to guess whether
the native C double format /is/ the big-endian or little-endian 754
double-precision format. If so, protocol >= 1 pickle in 2.5 uses much
simpler code to pack and unpack Python floats, simply copying from/to
native bytes verbatim (possibly reversing the byte order, depending on
platform endianness). Python doesn't even try to guess whether a C
double is "normal", or an inf, NaN, or signed zero then, so can't
screw that up -- it just copies the bits blindly.

That's much better on IEEE-754 boxes, although I bet it still has
subtle problems. For example, IIRC, 754 doesn't wholly define the
difference in storage formats for signaling NaNs versus quiet NaNs, so
I bet it's still theoretically possible to pickle a signaling NaN on
one 754 box and get back a quiet NaN (or vice versa) when unpickled on
a different 754 box.

Protocol 0 (formerly known as "text mode") pickles are still a crap
shoot for 754 special values, since there's still no consistency
across platforms in what the C string<->double routines produce or
accept for special values.

Now on to marshal. Before 2.5, marshal only had a "text mode" storage
format for Python floats, much like protocol=0 pickle. So, as for
pickle protocol 0, what marshal produced or reconstructed for a 754
special value was a platform-dependent accident.

Michael added a binary marshal format for Python floats in 2.5, which
uses the same code protocol >= 1 pickle uses for serializing and
unserializing Python floats (except that the marshal format is
little-endian instead of big-endian). These all go thru
floatobject.c's _PyFloat_Pack8 and _PyFloat_Unpack8 now, and a quick
glance at those will show that they take different paths according to
whether Michael's native-format-guessing code decided that the native
format was ieee_big_endian_format, ieee_little_endian_format, or
unknown_format. The long-winded pre-2.5 pack/unpack code is only used
in the unknown_format case now.
Aug 12 '06 #5
Tim Peters <ti********@gmail.comwrote:

[snip]

thanks for an exhaustively satisfying explanation!
Alex
Aug 12 '06 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
by: asmirnov1234567890 | last post by:
Hi my python 2.3.4 for windows refuse to execute line float("NaN"). It says: >>> float("NaN") Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: invalid literal for...
5
by: geskerrett | last post by:
We are working on a project to decipher a record structure of an old accounting system that originates from the late80's mid-90's. We have come across a number format that appears to be a "float"...
24
by: hjbortol | last post by:
Hi! Is the expression "a >= b" equivalent to "a - b >= 0" in C/C++? Is this equivalence an IEEE/ANSI rule? Or is this machine/compiler dependent? Any references are welcome! Thanks in...
3
by: Paul T. Rong | last post by:
Do "" and Null both mean nothing?¡¡ If I don't type anything in text box, the its value is Null£¿¡¡Or it is ¡°¡±£¿ I don¡¯ think they are the same, but I don¡¯t know their difference. Thanks.
14
by: TTroy | last post by:
Hello, can anyone explain why the following function will not work for INT_MIN: /* itoa: convert n to characters in s */ void itoa(int n, char s) { int i, sign; if((sign = n) < 0) /*...
12
by: Emi Lu | last post by:
Hello all, I have a question about "date" & "timestamp" types in PostgreSQL. I want to setup the default value '0000-00-00' and "0000-00-00 00:00:00" for them. However, it seems that PostgreSQL...
1
by: Peter Knörrich | last post by:
Hello, I've found another inconsistency, and looking through the list archives I can find mentions of funky stuff like print float('inf') giving Infanity
1
by: illegal.prime | last post by:
Hey all, I have an app, that could take two numbers of any type of numerical type int, long, double, float, uint, ulong, etc. I want to check that the numbers are part of a range that I consider...
30
by: Brian | last post by:
I am using Borland C++ Builder 5 for my application. When I build my application, it states that I have several if-statements that will always be false. The statements appear correct, so could...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.