Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Signed zeros: is this a bug?

2 views
Skip to first unread message

Mark Dickinson

unread,
Mar 11, 2007, 11:31:13 AM3/11/07
to
I get the following behaviour on Python 2.5 (OS X 10.4.8 on PowerPC,
in case it's relevant.)

>>> x, y = 0.0, -0.0
>>> x, y
(0.0, 0.0)
>>> x, y = -0.0, 0.0
>>> x, y
(-0.0, -0.0)

I would have expected y to be -0.0 in the first case, and 0.0 in the
second. Should the above be considered a bug, or is Python not
expected to honour signs of zeros? I'm working in a situation
involving complex arithmetic where branch cuts, and hence signed
zeros, are important, and it would be handy if the above code could be
relied upon to do the right thing.

Mark

Dan Bishop

unread,
Mar 11, 2007, 12:04:51 PM3/11/07
to

IIRC, float.__repr__ just does whatever libc does. Have you tried
using printf("%g, %g", 0.0, -0.0) in a C program?

jim-on-linux

unread,
Mar 11, 2007, 12:04:47 PM3/11/07
to pytho...@python.org, Mark Dickinson
On Sunday 11 March 2007 10:31, Mark Dickinson
wrote:

This works for some reason
instead of x,y = -0.0, 0.0
clumpy but the results are right.

x = -0.0
y= 0.0

x,y
(-0.0, 0.0)


jim-on-linux
http:\\inqvista.com

Terry Reedy

unread,
Mar 11, 2007, 12:13:36 PM3/11/07
to pytho...@python.org

"Dan Bishop" <dan...@yahoo.com> wrote in message
news:1173629090....@30g2000cwc.googlegroups.com...

| On Mar 11, 9:31 am, "Mark Dickinson" <dicki...@gmail.com> wrote:
| > I get the following behaviour on Python 2.5 (OS X 10.4.8 on PowerPC,
| > in case it's relevant.)
| >
| > >>> x, y = 0.0, -0.0
| > >>> x, y
| > (0.0, 0.0)
| > >>> x, y = -0.0, 0.0
| > >>> x, y
| >
| > (-0.0, -0.0)
|| IIRC, float.__repr__ just does whatever libc does. Have you tried
| using printf("%g, %g", 0.0, -0.0) in a C program?

Detailed FP behavior like this is system (and yes, libc) dependent. On
WinXP
IDLE 1.1.3
>>> x,y = 0.0, -0.0
>>> x,y
(0.0, 0.0)
>>> x,y = -0.0, 0.0
>>> x,y
(0.0, 0.0)
>>> -0.0
0.0

Terry Jan Reedy

Mark Dickinson

unread,
Mar 11, 2007, 12:37:32 PM3/11/07
to
On Mar 11, 12:13 pm, "Terry Reedy" <tjre...@udel.edu> wrote:
> "Dan Bishop" <danb...@yahoo.com> wrote in message

Interesting. So on Windows there's probably no hope of what I want to
do working.
But on a platform where the C library does the right thing with signed
zeros, this
behaviour is still a little surprising. I guess what's happening is
that there's
some optimization that avoids creating two separate float objects for
a float literal
that appears twice, and that optimization doesn't see the difference
between 0. and -0.

>>> x, y = 0., -0.
>>> id(x) == id(y)
True

jim-on-linux's solution works in the interpreter, but not in a script,
presumably because we've got file-wide optimization rather than
line-by-line optimization.

#test.py
x = -0.0
y = 0.0
print x, y
#end test.py

>>> import test
-0.0 -0.0

Mark

Duncan Booth

unread,
Mar 11, 2007, 1:21:10 PM3/11/07
to
"Mark Dickinson" <dick...@gmail.com> wrote:

> I guess what's happening is that there's some optimization that avoids
> creating two separate float objects for a float literal that appears
> twice, and that optimization doesn't see the difference between 0. and
> -0.

Don't guess. Test.

>>> def f():
x = 0.0
y = -0.0
return x, y

>>> dis.dis(f)
2 0 LOAD_CONST 1 (0.0)
3 STORE_FAST 0 (x)

3 6 LOAD_CONST 1 (0.0)
9 STORE_FAST 1 (y)

4 12 LOAD_FAST 0 (x)
15 LOAD_FAST 1 (y)
18 BUILD_TUPLE 2
21 RETURN_VALUE

Yes. Just the one constant there.


Tim Peters wrote in
http://blog.gmane.org/gmane.comp.python.devel/day=20050409:

> All Python behavior in the presence of a NaN, infinity, or signed zero
> is a platform-dependent accident. This is because C89 has no such
> concepts, and Python is written to the C89 standard. It's not easy to
> fix across all platforms (because there is no portable way to do so in
> standard C), although it may be reasonably easy to fix if all anyone
> cares about is gcc and MSVC (every platform C compiler has its own set
> of gimmicks for "dealing with" these things).

Alex Martelli

unread,
Mar 11, 2007, 1:26:57 PM3/11/07
to
Mark Dickinson <dick...@gmail.com> wrote:

Looks to me like you've found a bug with the parser/compiler:

>>> c=compile('-0.0,0.0', 'eval', 'eval')
>>> eval(c)
(-0.0, -0.0)
>>> dis.dis(c)
1 0 LOAD_CONST 1 ((-0.0, -0.0))
3 RETURN_VALUE

Similar problems appear in parsing display forms of lists and dicts and
calls to functions with constant arguments (among which a constant 0.0
and a constant -0.0) -- and more generally when both constants -0.0 and
0.0 appear within the same "unit of compilation" (expression, function,
...) as for example in:

>>> def f():
... a = -0.0
... return a, 0.0
...
>>> f()
(-0.0, -0.0)
>>>

Why I think it has specifically to do with parsing/compiling, e.g.:

>>> {23:-0.0, 45:0.0}
{45: -0.0, 23: -0.0}
>>> x=0.0
>>> y=-0.0
>>> {23:x, 45:y}
{45: -0.0, 23: 0.0}
>>>

so there appears to be no problem once the 0.0 and -0.0 values come from
variables or the like -- only if they're both from constants within the
same "unit of compilation". Indeed, if you put the code above in a
function, rather than in separate entries to the toplevel
interpreter...:

>>> def g():
... x = 0.0
... y = -0.0
... return {23:x, 45:y}
...
>>> g()
{45: 0.0, 23: 0.0}
>>>

...the bug surfaces again. Looks a bit like the result of a slightly
errant "peephole optimizer" pass which tries to roll multiple
occurrences of "the same constant" within the same codeobject into a
single one, and consider two immutables a "multiple occurrence" if
they're == to each other (as, indeed, 0.0 and -0.0 must be).

The Python-coded compiler package does NOT give the problem:

>>> compiler.compile('-0.0,0.0','zap','eval')
<code object <expression> at 0x70068, file "zap", line 1>
>>> dis.dis(_)
1 0 LOAD_CONST 1 (0.0)
3 UNARY_NEGATIVE
4 LOAD_CONST 1 (0.0)
7 BUILD_TUPLE 2
10 RETURN_VALUE

...presumably because it doesn't do peephole optimization (nor any other
kind of optimization).

Unfortunately I've never really looked in depth into these parts of
Python so I can't help much; however, if you open a bug at
<http://sourceforge.net/tracker/?group_id=5470> I think you stand a good
chance of eventually seeing it fixed in 2.5.x for some x.
Python/peephole.c does appear to be taking some care in constant
folding:

192 case UNARY_NEGATIVE:
193 /* Preserve the sign of -0.0 */
194 if (PyObject_IsTrue(v) == 1)
195 newconst = PyNumber_Negative(v);

so I suspect the problem is elsewhere, but I can't find it yet.


Alex


In the meantime, I hope that some available workarounds for the bug are
clear from this discussion: avoid using multiple constants in a single
compilation unit where one is 0.0 and another is -0.0, or, if you really
can't avoid that, perhaps use compiler.compile to explicitly build the
bytecode you need.

Mark Dickinson

unread,
Mar 11, 2007, 1:33:29 PM3/11/07
to
On Mar 11, 1:21 pm, Duncan Booth <duncan.bo...@invalid.invalid> wrote:

> Tim Peters wrote inhttp://blog.gmane.org/gmane.comp.python.devel/day=20050409:
>
> > All Python behavior in the presence of a NaN, infinity, or signed zero
> > is a platform-dependent accident. This is because C89 has no such
> > concepts, and Python is written to the C89 standard. It's not easy to
> > fix across all platforms (because there is no portable way to do so in
> > standard C), although it may be reasonably easy to fix if all anyone
> > cares about is gcc and MSVC (every platform C compiler has its own set
> > of gimmicks for "dealing with" these things).

Understood. Platform dependent is fine. But does this really excuse
results
like the following?

>>> from math import atan2
>>> x = -0.; print atan2(0., -1.)
-3.14159265359
>>> print atan2(0., -1.)
3.14159265359

Mark

Alex Martelli

unread,
Mar 11, 2007, 1:33:36 PM3/11/07
to
Duncan Booth <duncan...@invalid.invalid> wrote:

> "Mark Dickinson" <dick...@gmail.com> wrote:
>
> > I guess what's happening is that there's some optimization that avoids
> > creating two separate float objects for a float literal that appears
> > twice, and that optimization doesn't see the difference between 0. and
> > -0.
>
> Don't guess. Test.
>
> >>> def f():
> x = 0.0
> y = -0.0
> return x, y
>
> >>> dis.dis(f)
> 2 0 LOAD_CONST 1 (0.0)
> 3 STORE_FAST 0 (x)
>
> 3 6 LOAD_CONST 1 (0.0)
> 9 STORE_FAST 1 (y)
>
> 4 12 LOAD_FAST 0 (x)
> 15 LOAD_FAST 1 (y)
> 18 BUILD_TUPLE 2
> 21 RETURN_VALUE
>
> Yes. Just the one constant there.

And yet, as I wrote in a parallel post (the result of quite some
exploration), Python/peephole.c takes specific precautions against
improperly "constant folding" for the unary-minus of 0.0 -- the
collapsing of 0.0 and -0.0 into the "same" constant must happen
elsewhere (I haven't found out where, yet) and doesn't happen in the
Python-coded compiler module. So (on platforms where the underlying C
libraries do the right thing) I think this specific "overzealous
constant folding" must be considered a small bug -- and should be easy
to fix (by specialcasing -0.0 like Python/peephole.c does) if I could
but find out where in the Python sources the folding is happening...


Alex

Mark Dickinson

unread,
Mar 11, 2007, 1:44:34 PM3/11/07
to
On Mar 11, 1:26 pm, a...@mac.com (Alex Martelli) wrote:
> [Long analysis of probable cause of the problem]

Thank you for this. I was suspecting something along these lines,
but I don't yet know my way around the source well enough to figure
out where the problem was coming from.

> In the meantime, I hope that some available workarounds for the bug are
> clear from this discussion: avoid using multiple constants in a single
> compilation unit where one is 0.0 and another is -0.0, or, if you really
> can't avoid that, perhaps use compiler.compile to explicitly build the
> bytecode you need.

Yup: the workaround seems to be as simple as replacing all occurrences
of -0.0 with -(0.0). I'm embarrassed that I didn't figure this out
sooner.

>>> x, y = -(0.0), 0.0
>>> x, y
(-0.0, 0.0)

Mark

jim-on-linux

unread,
Mar 11, 2007, 2:03:53 PM3/11/07
to pytho...@python.org, Mark Dickinson

#########


This is the only way I could make this work in a
script.

from decimal import Decimal

x = Decimal( "-0.0")
y= Decimal("0.0")
print x,y


x = Decimal( "0.0")
y= Decimal("-0.0")
print x,y

jim-on-linux
http:\\www.inqvista.com


Alex Martelli

unread,
Mar 11, 2007, 2:26:01 PM3/11/07
to
Mark Dickinson <dick...@gmail.com> wrote:

> On Mar 11, 1:26 pm, a...@mac.com (Alex Martelli) wrote:
> > [Long analysis of probable cause of the problem]
>
> Thank you for this. I was suspecting something along these lines,
> but I don't yet know my way around the source well enough to figure
> out where the problem was coming from.

The parser/compiler/etc are unfortunately some of the hardest parts of
the sources -- I'm not all that familiar with that part myself, which is
why it took me quite some digging.


> > In the meantime, I hope that some available workarounds for the bug are
> > clear from this discussion: avoid using multiple constants in a single
> > compilation unit where one is 0.0 and another is -0.0, or, if you really
> > can't avoid that, perhaps use compiler.compile to explicitly build the
> > bytecode you need.
>
> Yup: the workaround seems to be as simple as replacing all occurrences
> of -0.0 with -(0.0). I'm embarrassed that I didn't figure this out
> sooner.
>
> >>> x, y = -(0.0), 0.0
> >>> x, y
> (-0.0, 0.0)

Glad it works for you, but it's the kind of workaround that could break
with any minor tweak/optimization to the compiler... very fragile:-(.

I think i found the cause of the bug, BTW. The collection of constants
in a code object is built in Python/compile.c and it's built as a
dictionary, field u_consts in struct compiler_unit. The "visitor" for
an expression that is a number is (in a case statement)

case Num_kind:
ADDOP_O(c, LOAD_CONST, e->v.Num.n, consts);
break;

(line 2947 of compile.c in Python's current sources from svn). ADDOP_O
just calls compiler_addop_o, which in turn does compiler_add_o before
adding the opcode (LOAD_CONST)

compiler_add_o (at lines 903-933) is used for all of the temporary
dictionaries in compiler_unit; a Python equivalent, basically, would be:

def eqv_cao(somedict, someobj):
# make sure types aren't coerced, e.g. int and long
t = someobj, type(someobj)
if t in somedict:
return somedict[t]
somedict[t] = index = len(somedict)
return index

a simple and fast way to provide a distinct numeric index (0 and up) to
each of a bunch of (hashable) objects.

Alas, here is the problem...: 0.0 and -0.0 are NOT separate as dict
keys! They are == to each other. So are 0, 0L, and 0+j0, but the
compiler smartly distinguishes these cases by using (obj, type) as the
key (the *types* are distinguished, even though the *values*) are; this
doesn't help with 0.0 and -0.0 since both have type float.

So, the first occurrence of either 0.0 or -0.0 in the compilation unit
ends up in the table of constants, and every other occurrence of either
value later in the unit is mapped to that one constant value:-(

This is not trivial to fix cleanly...:-(. compiler_add_o would have to
test "is the object I'm storing -0.0" (I don't even know how to do that
in portable C...) and then do some kludge -- e.g. use as the key into
the dict (-0.0, 0) instead of (-0.0, float) for this one special case.
(I think the table of constants would still be emitted OK, since the
type part of the key is elided anyway in that table as placed in the
bytecode). Or maybe we should give up ever storing -0.0 in the tables
of constant and ALWAYS have "0.0, unary-minus" wherever it appears (that
would presumably require working on the AST-to-bytecode visitors that
currently work ever-so-slightly-differently for this specific
troublespot in the C-coded version vs the Python-coded one...).

If you know the proper way to test for -0.0 in portable C code (or some
feature macro to use in a #if to protect nonportable code) I could try
proposing the first of these two solutions as a patch (I'm not going to
keep delving into that AST and visitors much longer...:-), but I suspect
it would be rejected as "too tricky [and minisculely slowing down every
compilation] for something that's too much of special case [and Python
does not undertake to support in general anyway]". Still, we can't be
sure unless we try (and maybe somebody can think of a cleaner
workaround...).


Alex

Alex Martelli

unread,
Mar 11, 2007, 2:59:53 PM3/11/07
to
Alex Martelli <al...@mac.com> wrote:
...

> > Yup: the workaround seems to be as simple as replacing all occurrences
> > of -0.0 with -(0.0). I'm embarrassed that I didn't figure this out
> > sooner.
> >
> > >>> x, y = -(0.0), 0.0
> > >>> x, y
> > (-0.0, 0.0)
>
> Glad it works for you, but it's the kind of workaround that could break
> with any minor tweak/optimization to the compiler... very fragile:-(.

OTOH, Python 2.4 works just fine...:

Python 2.4.3 (#1, Apr 7 2006, 10:54:33)
[GCC 4.0.1 (Apple Computer, Inc. build 5250)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.0,-0.0
(0.0, -0.0)
>>> -0.0,0.0
(-0.0, 0.0)

so it seems to be very specifically a 2.5 problem.


Alex

Scott David Daniels

unread,
Mar 11, 2007, 3:12:27 PM3/11/07
to
Alex Martelli wrote:
> Alas, here is the problem...: 0.0 and -0.0 are NOT separate as dict
> keys! They are == to each other. So are 0, 0L, and 0+j0, but the
> compiler smartly distinguishes these cases by using (obj, type) as the
> key (the *types* are distinguished, even though the *values*) are; this
> doesn't help with 0.0 and -0.0 since both have type float....

> If you know the proper way to test for -0.0 in portable C code (or some
> feature macro to use in a #if to protect nonportable code) ....

Perhaps you could add a type "NegativeFloat" -- a subtype of float,
and distinguish negative zero from zero that way. Not saying I know how
in portable C, but ....

--Scott David Daniels
scott....@acm.org

Mark Dickinson

unread,
Mar 11, 2007, 3:46:08 PM3/11/07
to
On Mar 11, 2:59 pm, a...@mac.com (Alex Martelli) wrote:
> [...]

> OTOH, Python 2.4 works just fine...:
>
> Python 2.4.3 (#1, Apr 7 2006, 10:54:33)
> [GCC 4.0.1 (Apple Computer, Inc. build 5250)] on darwin
> Type "help", "copyright", "credits" or "license" for more information.>>> 0.0,-0.0
> (0.0, -0.0)
> >>> -0.0,0.0
>
> (-0.0, 0.0)
>
> so it seems to be very specifically a 2.5 problem.

I've filed a bug report (bug #1678380) and got an impressively
quick response from MvL. It looks like this is a `won't fix'.
Oh well.

Thanks for all your help.

Mark

Gabriel Genellina

unread,
Mar 11, 2007, 3:48:04 PM3/11/07
to pytho...@python.org
En Sun, 11 Mar 2007 15:26:01 -0300, Alex Martelli <al...@mac.com> escribió:

> Or maybe we should give up ever storing -0.0 in the tables
> of constant and ALWAYS have "0.0, unary-minus" wherever it appears (that
> would presumably require working on the AST-to-bytecode visitors that
> currently work ever-so-slightly-differently for this specific
> troublespot in the C-coded version vs the Python-coded one...).

I think that way is the less intrusive, doesn't rely on a particular FP
implementation, and the more likely to be accepted.
Looking at ast.c, the culprit is some optimization in ast_for_factor, line
1506
/* If the unary - operator is applied to a constant, don't generate
a UNARY_NEGATIVE opcode. Just store the approriate value as a
constant. The peephole optimizer already does something like
this but it doesn't handle the case where the constant is
(sys.maxint - 1). In that case, we want a PyIntObject, not a
PyLongObject.
*/

After the long "if", I would use parsenumber(STR(pnum)) and check the type
and value of the resulting object; if it's a float and 0.0, skip the
optimization and continue below as a normal case.
Unfortunately I'm not able to compile and test the code right now, so I
can't provide an actual patch. (At least, not until tuesday). But I hope
this simple comment is useful anyway...

(I cannot find peephole.c on the source distribution for Python 2.5, but
you menctioned it on a previous message, and the comment above refers to
the peephole optimizer... where is it?)

--
Gabriel Genellina

Tim Peters

unread,
Mar 11, 2007, 5:15:59 PM3/11/07
to
[attribution lost]

...
>>> Yup: the workaround seems to be as simple as replacing all
occurrences
>>> of -0.0 with -(0.0). I'm embarrassed that I didn't figure this out
>>> sooner.
>>>
>>> >>> x, y = -(0.0), 0.0
>>> >>> x, y
>>> (-0.0, 0.0)

[Alex Martelli]


>> Glad it works for you, but it's the kind of workaround that could
>> break with any minor tweak/optimization to the compiler...
>> very fragile:-(.

[also Alex]


> OTOH, Python 2.4 works just fine...:
>
> Python 2.4.3 (#1, Apr 7 2006, 10:54:33)
> [GCC 4.0.1 (Apple Computer, Inc. build 5250)] on darwin
> Type "help", "copyright", "credits" or "license" for more information.
> >>> 0.0,-0.0
> (0.0, -0.0)
> >>> -0.0,0.0
> (-0.0, 0.0)
>
> so it seems to be very specifically a 2.5 problem.

It's a bug that keeps resurfacing, probably because there's no portable
way to test that it stays fixed :-( (it's not an accident that the OP
relied on atan2 to distinguish +0.0 from -0.0! they act the same in
/almost/ all contexts).

Python's grammar doesn't have negative numeric literals. Instead

"-" CONSTANT_LITERAL

looks like unary minus applied to the non-negative CONSTANT_LITERAL.
All early versions of Python worked that way too.

The first time the +/- 0.0 bug occurred is when the front end was
changed to act as if

"-" CONSTANT_LITERAL

were a literal in its own right, and then that +0.0 == -0.0 caused the
first instance of either in a compilation unit to be used as the value
for all instances of both throughout the compilation unit. That was
fixed by refusing to apply the optimimization if the value of
CONSTANT_LITERAL was a float that compared equal to 0.0.

2.5 introduced a new front end and more ambitious constant-folding, and
I expect the bug showed up again due to one of those.

Note: printing the value of a float 0 may not reveal its sign. IIRC,
glibc's float-to-string routines do display the sign of 0, but
Microsoft's do not. However, atan2() is sensitive to the sign under
both glibm and Microsoft's libm equivalent.

Message has been deleted

Paul Rubin

unread,
Mar 11, 2007, 5:22:56 PM3/11/07
to
Tim Peters <tim...@comcast.net> writes:
> 2.5 introduced a new front end and more ambitious constant-folding, and
> I expect the bug showed up again due to one of those.

I hope it does get fixed. Not having referential transparency in a
basic math function like atan2 is pretty disturbing in a modern
computer language.

Paul Rubin

unread,
Mar 11, 2007, 5:28:15 PM3/11/07
to
Dennis Lee Bieber <wlf...@ix.netcom.com> writes:
> Pardon? What is "the right thing with signed zeros"... In the last
> 30 years I've been on machines that normalize floating zero into a true
> zero (all bits are zero: mantissa, exponent, and sign). This is the
> first time I've even seen a variable output as a negative 0.0!

Most machines these days use IEEE 754 which supports negative zero.

http://en.wikipedia.org/wiki/Negative_zero

Mark Dickinson

unread,
Mar 11, 2007, 5:29:48 PM3/11/07
to
On Mar 11, 5:15 pm, Tim Peters <tim....@comcast.net> wrote:
It's a bug that keeps resurfacing, probably because there's no
portable
> way to test that it stays fixed :-( (it's not an accident that the OP
> relied on atan2 to distinguish +0.0 from -0.0! they act the same in
> /almost/ all contexts).

Not an accident, but not a contrived example either. I'd rewritten
all the cmath routines (in pure Python) to be more numerically aware
and use the
`correct' (= those recommended by Kahan) branch cuts. It was in
writing the unit tests for this that the problems above surfaced.

By the way, I don't suppose that anybody would be interested in
a rewritten cmath for Python 2.6? It wouldn't be hard for me to
rewrite what I already have in C, and add suitable documentation
and tests.

Mark

Paul Rubin

unread,
Mar 11, 2007, 6:49:05 PM3/11/07
to
"Mark Dickinson" <dick...@gmail.com> writes:
> By the way, I don't suppose that anybody would be interested in
> a rewritten cmath for Python 2.6? It wouldn't be hard for me to
> rewrite what I already have in C, and add suitable documentation
> and tests.

I can't speak for the developers but my personal opinion is that this
is worthwhile. I'm pretty Common Lisp and Scheme specify the branch
cuts and I believe Java does the same. That is to help with the
consistency and predicability of program behavior as well as to help
various numerical algorithms. C has a different goal, which is to be
a semi-portable assembly language, putting priority instead on
minimizing intermediation between the programmer and the machine,
instead of on predicability. Python should take the approach of the
higher level languages and implement this stuff precisely, instead of
just going along with whatever loose semantics the underlying C
implementation happens to supply.

Paddy

unread,
Mar 11, 2007, 6:49:24 PM3/11/07
to
On Mar 11, 9:28 pm, Paul Rubin <http://phr...@NOSPAM.invalid> wrote:

Isn't negative zero mathematically the same as zero? Isn't -0 just an
artefact of the representation of floating point numbers? Shouldn't
f(0) == f(-0) for all functions f?
I read the wikipedia article about meteorologists using -0 to denote a
negative number rounded to zero for the purposes of binning, i.e. if
you want to tally days with temperature above and below zero, but it
doesn't seem right. You should arrange for any temperature to be in
only one range and record temperatures to their determined accuracy. a
temperature of zero would only be in one bin and if a temperature is
read as -0.2 and th rounding says it should be taken as zero then it
should go in the same bin as any positive reading that is rounded to
zero.
Getting back to Python, shouldn't we strive to remove any distinction?
a zero is a zero regardless of sign and a function like atan returning
one of two different vaues for an argument of zero is actually
mathematically not a bad thing to do?

- Paddy.

Michael Spencer

unread,
Mar 11, 2007, 6:58:07 PM3/11/07
to pytho...@python.org
Gabriel Genellina wrote:

>
> (I cannot find peephole.c on the source distribution for Python 2.5, but
> you menctioned it on a previous message, and the comment above refers to
> the peephole optimizer... where is it?)
>

The peephole optimizer is in compile.c - the entry point is optimize_code

BTW, I have written a pure-Python compiler which aims to be functionally
identical to the Python 2.5 compiler, and is in fact very similar (much closer
than stdlib.compiler). It may be helpful in investigating alternative
workarounds for the -0.0 issue.

http://svn.brownspencer.com/pycompiler/branches/new_ast/

Michael

Paul Rubin

unread,
Mar 11, 2007, 7:21:38 PM3/11/07
to
"Paddy" <padd...@googlemail.com> writes:
> > Most machines these days use IEEE 754 which supports negative zero.
> > http://en.wikipedia.org/wiki/Negative_zero
>
> Isn't negative zero mathematically the same as zero? Isn't -0 just an
> artefact of the representation of floating point numbers? Shouldn't
> f(0) == f(-0) for all functions f?

Not sure what you mean. "Floating point numbers" and in particular
"IEEE 754 floating point numbers" are a set of values and operations
with particular characteristics, that computers happen to implement.
They are not the same thing as the mathematical real numbers. For
example, there are infinitely many real numbers, but there are only
finitely many 64-bit IEEE floating point numbers (at most 2**64 of
them). They don't satisfy the same algebraic laws as real numbers.
For example, (1e100 + 1) == 1e100, and as a consequence, (1e100 + 1) -
1e100 == 0, but (1e100 - 1e100) + 1 == 1, so the commutative addition
law doesn't hold. These are all part of a mesh of interlocking
compromises made in floating point computer arithmetic to approximate
real-number arithmetic with finite-precision values. At first (i.e.
from the early computer era through the 1970's or so) this stuff was
designed somewhat ad hoc by computer architects, but eventually
serious numerical mathemticians got into the act, figuring out how to
make the best possible choices of these compromises for numerical
computation. The result was IEEE 754, which resulted in Prof. Kahan
winning the Turing award in 1989.

IEEE 754 specifies that -0 and +0 are separate numbers. Yes it is an
artifact in the sense of being one of the compromises. But these
compromises all interact with each other to make the errors cancel in
various numerical algorithms. The existence of -0 in IEEE 754 is part
of an intricate framework designed very carefully over a long period
by extremely experienced and knowledgeable people who knew what they
were doing. It's not really wise to mess with it unless you're
planning to undertake a project to redesign computer arithmetic of
similar scope to the IEEE 754 effort.

> Getting back to Python, shouldn't we strive to remove any distinction?
> a zero is a zero regardless of sign and a function like atan returning
> one of two different vaues for an argument of zero is actually
> mathematically not a bad thing to do?

No. Floating point numbers are not the same as real numbers and they
don't satisfy the same laws. There have been some proposals
(rejected) for Python to support exact rational arithmetic in addition
to floating point and exact integer arithmetic. Exact rationals in
Python (if implemented) should behave like mathematical rationals.
But Python floating point arithmetic should follow IEEE 754, at least
when the hardware supports it, which these days is almost always.

André

unread,
Mar 11, 2007, 7:35:23 PM3/11/07
to
On Mar 11, 7:49 pm, "Paddy" <paddy3...@googlemail.com> wrote:
>
> Isn't negative zero mathematically the same as zero? Isn't -0 just an
> artefact of the representation of floating point numbers? Shouldn't
> f(0) == f(-0) for all functions f?

Read the original post again... The relevant part is:
====


I'm working in a situation
involving complex arithmetic where branch cuts, and hence signed
zeros, are important, and it would be handy if the above code could be
relied upon to do the right thing.

====

You may want to read about branch cuts and complex arithmetics. I
don't mean this as a putdown - simply as a useful suggestion for you
to read.

André

Alex Martelli

unread,
Mar 11, 2007, 9:17:48 PM3/11/07
to
Gabriel Genellina <gags...@yahoo.com.ar> wrote:

> En Sun, 11 Mar 2007 15:26:01 -0300, Alex Martelli <al...@mac.com> escribió:
>
> > Or maybe we should give up ever storing -0.0 in the tables
> > of constant and ALWAYS have "0.0, unary-minus" wherever it appears (that
> > would presumably require working on the AST-to-bytecode visitors that
> > currently work ever-so-slightly-differently for this specific
> > troublespot in the C-coded version vs the Python-coded one...).
>
> I think that way is the less intrusive, doesn't rely on a particular FP
> implementation, and the more likely to be accepted.
> Looking at ast.c, the culprit is some optimization in ast_for_factor, line
> 1506
> /* If the unary - operator is applied to a constant, don't generate
> a UNARY_NEGATIVE opcode. Just store the approriate value as a
> constant. The peephole optimizer already does something like
> this but it doesn't handle the case where the constant is
> (sys.maxint - 1). In that case, we want a PyIntObject, not a
> PyLongObject.
> */
>
> After the long "if", I would use parsenumber(STR(pnum)) and check the type
> and value of the resulting object; if it's a float and 0.0, skip the
> optimization and continue below as a normal case.
> Unfortunately I'm not able to compile and test the code right now, so I
> can't provide an actual patch. (At least, not until tuesday). But I hope
> this simple comment is useful anyway...

Yep, it sure might, if I can make time to act on it:-).


> (I cannot find peephole.c on the source distribution for Python 2.5, but
> you menctioned it on a previous message, and the comment above refers to
> the peephole optimizer... where is it?)

http://svn.python.org/view/python/trunk/Python/peephole.c?rev=54086&view
=auto

to browse the current version on the trunk. Right under the Python
toplevel subdirectory in the sources, IOW.


Alex

Alex Martelli

unread,
Mar 11, 2007, 9:56:07 PM3/11/07
to
Alex Martelli <al...@mac.com> wrote:
...
> Yep, it sure might, if I can make time to act on it:-).

...and I did -- patch 1678668 is right there, brand newm at
<http://sourceforge.net/tracker/index.php> .

I hope it also satisfies the timbot's very reasonable lament about the
bug resurfacing once in a while, since it included one more unittest to
check whether the bug is there (TDD rocks!-).


Alex

Alex Martelli

unread,
Mar 11, 2007, 9:56:08 PM3/11/07
to
Tim Peters <tim...@comcast.net> wrote:
...

> It's a bug that keeps resurfacing, probably because there's no portable
> way to test that it stays fixed :-( (it's not an accident that the OP
> relied on atan2 to distinguish +0.0 from -0.0! they act the same in

Please take a look at my patch 1678668, brand new at
<http://sourceforge.net/tracker/index.php> and as yet unassigned (hint,
hint:-).

I hope it satisfies your very reasonable lament about the bug


resurfacing once in a while, since it included one more unittest to

check whether the bug is there (TDD rocks!-), based exactly on the
behavior of atan2 (only on IEEE-format machines, though).

> 2.5 introduced a new front end and more ambitious constant-folding, and
> I expect the bug showed up again due to one of those.

Yep, in ast.c's ast_for_factor -- it lacks the specialcasing that
peephole.c does have (and my patch essentially adds it back).

If it's worth specialcasing in peephole.c (and I strongly agree with
Raymond's implicit opinion that it is), it should be just as worth
specialcasing in ast.c, no?-)


Alex

Alex Martelli

unread,
Mar 11, 2007, 9:56:08 PM3/11/07
to
Mark Dickinson <dick...@gmail.com> wrote:

That bug isn't formally closed yet, and the discussion's going on, we'll
see. Meanwhile can you try my patch 1678668 just to double check it
does fix everything? (It's agains the current HEAD of Python's svn).


Alex

Alex Martelli

unread,
Mar 11, 2007, 9:59:43 PM3/11/07
to
Paul Rubin <http://phr...@NOSPAM.invalid> wrote:

> (rejected) for Python to support exact rational arithmetic in addition
> to floating point and exact integer arithmetic. Exact rationals in
> Python (if implemented) should behave like mathematical rationals.
> But Python floating point arithmetic should follow IEEE 754, at least
> when the hardware supports it, which these days is almost always.

Incidentally (and I know you know that, Paul, but others interested in
this thread might not) fast exact rational arithmetic (based on the LGPL
library named GMP) is supplied, among other functionality, by module
gmpy, currently found at http://code.google.com/p/gmpy/ (this version is
more recent than the older one at sourceforce, which for some reason
doesn't let me update things right any more).


Alex

Jorge Godoy

unread,
Mar 11, 2007, 10:12:04 PM3/11/07
to
al...@mac.com (Alex Martelli) writes:

> Incidentally (and I know you know that, Paul, but others interested in
> this thread might not) fast exact rational arithmetic (based on the LGPL
> library named GMP) is supplied, among other functionality, by module
> gmpy, currently found at http://code.google.com/p/gmpy/ (this version is
> more recent than the older one at sourceforce, which for some reason
> doesn't let me update things right any more).

For some reason setuptools isn't finding it:

jupiter:~ # easy_install gmpy
Searching for gmpy
Reading http://cheeseshop.python.org/pypi/gmpy/
Reading http://code.google.com/p/gmpy/
Reading http://cheeseshop.python.org/pypi/gmpy/1.02
No local packages or download links found for gmpy
error: Could not find suitable distribution for Requirement.parse('gmpy')


I believe that setuptools isn't ready to find packages on code.google.com
yet... ;-)

--
Jorge Godoy <jgo...@gmail.com>

Alex Martelli

unread,
Mar 11, 2007, 10:34:44 PM3/11/07
to
Jorge Godoy <jgo...@gmail.com> wrote:

I'm not familiar with setuptools, what exactly is it looking for?

If it's looking for stuff to download it should start at

http://code.google.com/p/gmpy/downloads/list

(but what does it want -- the sources' zipfile, or some binary, and in
what format and with what naming convention?) -- I'll be glad to edit
the URL for gmpy at cheeseshop if some setuptools expert can explain
these subtleties... thanks for pointing out that there's a problem
btw!-)


Alex

Jorge Godoy

unread,
Mar 11, 2007, 10:47:45 PM3/11/07
to
al...@mac.com (Alex Martelli) writes:

> I'm not familiar with setuptools, what exactly is it looking for?
>
> If it's looking for stuff to download it should start at
>
> http://code.google.com/p/gmpy/downloads/list
>
> (but what does it want -- the sources' zipfile, or some binary, and in
> what format and with what naming convention?) -- I'll be glad to edit
> the URL for gmpy at cheeseshop if some setuptools expert can explain
> these subtleties... thanks for pointing out that there's a problem
> btw!-)

Thanks for caring ;-)


It looks for several things: eggs (for the Python version it is being used),
zipfiles and tarballs (I dunno if it looks for more things).

If it finds, for example, gmpy-1.0.2-py2.4.egg it won't install here 'cause I
use Python 2.5 and then it will continue searching for gmpy-1.0.2-py2.5.egg or
an alternative format that can be used. The last resort is the tarball / zip
with the sources so that the package can be rebuilt.

Probably other people that are more experienced with it can help more. I'm
more an end user of it and I know the essential for my needs.

I just pointed out because setuptools helps a lot on obtaining a package and
installing it (even if there's some building needed before installing).


Sorry for not being able to help more.


With regards to the hyperlink, I believe that if the link on Pypi was to the
URL above it would be easier. Another alternative is a link at the first
page. And, of course, the last alternative is teaching setuptools how to work
with code.google.com -- if it doesn't already know -- as it learnt how to work
with SourceForge and its "random" mirrors. I don't know how to write that
code, though.


Be seeing you,
--
Jorge Godoy <jgo...@gmail.com>

Alex Martelli

unread,
Mar 11, 2007, 11:07:19 PM3/11/07
to
Jorge Godoy <jgo...@gmail.com> wrote:
...

> > these subtleties... thanks for pointing out that there's a problem
> > btw!-)
>
> Thanks for caring ;-)

Hey, I'd just love to make it as easy as possible to get gmpy -- that's
why I (shudder!-) even build and upload Windows installers...


> It looks for several things: eggs (for the Python version it is being used),
> zipfiles and tarballs (I dunno if it looks for more things).
>
> If it finds, for example, gmpy-1.0.2-py2.4.egg it won't install here 'cause I
> use Python 2.5 and then it will continue searching for gmpy-1.0.2-py2.5.egg or
> an alternative format that can be used. The last resort is the tarball / zip
> with the sources so that the package can be rebuilt.

OK, the .zip file IS there -- I don't know how to build .egg ones but of
course I could study up on it -- can you suggest a URL?

To be usable on Windows w/o effort, the packaged format must contain a
.pyd (and, on Mac, a .so, etc). Can a .egg deal w/that?

I need to find out w/certainty, because once I've uploaded a file w/a
certain name I can't change the name, nor the contents -- the URLs on
code.google.com are meant to be permanent...

> Probably other people that are more experienced with it can help more. I'm
> more an end user of it and I know the essential for my needs.
>
> I just pointed out because setuptools helps a lot on obtaining a package and
> installing it (even if there's some building needed before installing).

OK, but since most Windows users don't have a suitable C compiler, and
many Mac ones never bother installing the C compiler that comes with
their OS DVDs, if I'm to make things easy I definitely need to pack up
binaries. How do I pack binaries (for dynamic link libraries) that need
to be very different on Win, Mac, various Linux distros...?

> Sorry for not being able to help more.
>
>
> With regards to the hyperlink, I believe that if the link on Pypi was to the
> URL above it would be easier. Another alternative is a link at the first
> page. And, of course, the last alternative is teaching setuptools how to work
> with code.google.com -- if it doesn't already know -- as it learnt how to work
> with SourceForge and its "random" mirrors. I don't know how to write that
> code, though.

Me neither, knowing near to nothing about setuptools (I'm not even a
user of it...). Let's hope some expert does speak up -- I can't just
freely experiment with uploads and the like...


Alex

Mark Dickinson

unread,
Mar 12, 2007, 12:03:46 AM3/12/07
to
On Mar 11, 9:56 pm, a...@mac.com (Alex Martelli) wrote:
> That bug isn't formally closed yet, and the discussion's going on, we'll
> see. Meanwhile can you try my patch 1678668 just to double check it
> does fix everything? (It's agains the current HEAD of Python's svn).

It does, and all tests pass. (Still on OS X 10.4.8/PowerPC; I'll
test
it tomorrow on my Linux machine at work.)

Mark

Paddy

unread,
Mar 12, 2007, 1:35:25 AM3/12/07
to

A big thanks to Paul and Andre,
I think I have it now. The OP is investigating multivalued functions
where the function converges to different values when approached from
different directions. Floating point arithmetic being a best
compromise solution to the rational number system, distinguishes
between zero and minus zero as an important part of the compromise.
The OP wants to compute a function of zero and minus zero distinctly
and is hampered by Python not preserving the zero/minus zero
distinction in some cases - hence it being a bug.

Swell, Ta!

- Paddy.

Hey, I'm still learnin'. Sweet!

Jorge Godoy

unread,
Mar 12, 2007, 8:29:33 AM3/12/07
to
al...@mac.com (Alex Martelli) writes:

> Hey, I'd just love to make it as easy as possible to get gmpy -- that's
> why I (shudder!-) even build and upload Windows installers...

Brave man! :-)

> OK, the .zip file IS there -- I don't know how to build .egg ones but of
> course I could study up on it -- can you suggest a URL?

The .egg is nice because it can be an already built binary.

http://peak.telecommunity.com/DevCenter/setuptools

The URL above contains examples and instructions on how to use setuptools.

> To be usable on Windows w/o effort, the packaged format must contain a
> .pyd (and, on Mac, a .so, etc). Can a .egg deal w/that?

Yes. I have a .egg that has a .so here, for rpy (http://rpy.sf.net/).

> I need to find out w/certainty, because once I've uploaded a file w/a
> certain name I can't change the name, nor the contents -- the URLs on
> code.google.com are meant to be permanent...

Hmmm... I'd suggest you to build only locally then... I don't like this kind
of "permanent" things. They could have a flag "publish this" to make it
permanent and make the standard "unpublished". Here's a suggestion for you to
forward there inside Google... ;-)

> OK, but since most Windows users don't have a suitable C compiler, and
> many Mac ones never bother installing the C compiler that comes with
> their OS DVDs, if I'm to make things easy I definitely need to pack up
> binaries. How do I pack binaries (for dynamic link libraries) that need
> to be very different on Win, Mac, various Linux distros...?

One build per platform would be my choice. Packing everything together would
make the package too big (specially if you start supporting other OSs).

> Me neither, knowing near to nothing about setuptools (I'm not even a
> user of it...). Let's hope some expert does speak up -- I can't just
> freely experiment with uploads and the like...

Lets wait. ;-)

Thanks again,
--
Jorge Godoy <jgo...@gmail.com>

Ben Finney

unread,
Mar 12, 2007, 6:32:51 PM3/12/07
to pytho...@python.org
al...@mac.com (Alex Martelli) writes:

> This is not trivial to fix cleanly...:-(. compiler_add_o would have
> to test "is the object I'm storing -0.0" (I don't even know how to
> do that in portable C...) and then do some kludge -- e.g. use as the
> key into the dict (-0.0, 0) instead of (-0.0, float) for this one
> special case.

Another way, which avoids making this decision, is to always store the
sign *and* value of the number as part of the key. I don't know if
that tradeoff is the right one though.

--
\ "Our products just aren't engineered for security." -- Brian |
`\ Valentine, senior vice-president of Microsoft Windows |
_o__) development |
Ben Finney

Hendrik van Rooyen

unread,
Mar 13, 2007, 1:50:06 AM3/13/07
to pytho...@python.org
"Paddy" <pa...y3118@googlemail.com> wrote

>
> Hey, I'm still learnin'. Sweet!
>

contrary to popular belief, the answer to life,
the universe, happiness and everything is
not 42, but the above.

- Hendrik

0 new messages