classification
Title: copysign() with NaN arguments on OpenSolaris
Type: behavior Stage:
Components: Versions: Python 3.1, Python 3.2
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: mark.dickinson, skrah
Priority: normal Keywords:

Created on 2009-11-07 20:41 by skrah, last changed 2009-11-09 11:31 by mark.dickinson. This issue is now closed.

Messages (5)
msg95027 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2009-11-07 20:41
Sorry to report so many obscure corner cases. With the combination
Opensolaris/suncc/Python3.x copysign() gives reversed results when the
second argument is a NaN. The bug is in the C99 copysign() function
(OpenSolaris/suncc), but Python2.6 seems to have a workaround: 

Python 3.2a0 (py3k:76144, Nov  7 2009, 18:50:00) [C] on sunos5
Type "help", "copyright", "credits" or "license" for more information.
>>> from math import *
>>> copysign(1.0, float("nan"))
-1.0
>>> copysign(1.0, float("-nan"))
1.0

Python 2.6.2 (r262:71600, Nov  7 2009, 19:29:52) [C] on sunos5
Type "help", "copyright", "credits" or "license" for more information.
>>> from math import copysign
>>> copysign(1.0, float("nan"))
1.0
>>> copysign(1.0, float("-nan"))
-1.0
>>>
msg95033 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-11-08 00:30
You may have let yourself in for something here.  :-)

Can you tell me what:

>>> import sys
>>> print sys.float_repr_style

gives for your Opensolaris/suncc/Python3.x combination?  If it prints 
'legacy', then I'd dearly like some help with making it read 'short' 
instead.  That would then make treatment of nans on that combination 
consistent with other platforms.

All I really need to know is how to set the x87 (I'm assuming you're on 
32-bit x86 here) FPU control word using suncc.  Oh, and a willing victim 
to test changes.  Look for HAVE_PY_SET_53BIT_PRECISION in 
Include/pyport.h for details.


Having said that, I don't really see this difference with nans as an 
actual bug.  Is it possible that float("nan") is simply giving a nan 
with its sign bit set here, and that float("-nan") is giving a nan with 
no sign bit set?  That would actually be quite unsurprising, since 
according to Intel's manuals, the 'default' NaN value returned by 
invalid operations has its sign bit set.  It would also be (I think) 
quite legal:  sign bits of NaNs don't make a lot of sense anyway, and I 
don't think Python should specify anything about the sign bit (or indeed 
the payload) of the NaN coming from float('nan').

In other words, I think it's not unreasonable to regard the sign of
copysign(x, y) as undefined whenever y is a nan.  (I'm fairly sure that 
MPFR takes this attitude, for example.)
msg95041 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2009-11-08 13:49
I hope this won't be getting too complex. :)

Firstly, I agree that this is perhaps not a bug at all. I reported it
because I seemed possible that Python2.x had a deliberate workaround for
this issue which somehow got lost in 3.x.

Secondly, I didn't mention that I'm running OpenSolaris on KVM since I
can't get it to install on physical hardware. However, I don't think KVM
plays a role here since gcc produces 'normal' results.

So, the problem combination is:

Ubuntu64bit -> KVM -> OpenSolaris32bit/suncc/Py3k


On the C level, here's an example:

#include <stdio.h>
#include <math.h>

int main(void)
{
        double x = NAN;
        printf("%f  %f\n", x, copysign(1.0, x));
        return 0;
}

This gives -NaN, -1.000000 with suncc and NaN, 1.000000 with gcc. 


Back to Python:

sys.float_repr_style is 'legacy'.

It looks like the C99 fenv functions are pretty much supported, but I
don't know since when. I found man pages from 2003 for fenv and from
2006 for the fe*() functions.

This for example works:

#include <stdio.h>
#include <stdio.h>
#include <fenv.h>
#include <assert.h>

int main(void)
{
        #pragma STDC FENV_ACCESS ON
        int save_round;
        int setround_ok;

        save_round = fegetround();

        setround_ok = fesetround(FE_DOWNWARD);
        assert(setround_ok == 0);

        fesetround(save_round);
}


I could test changes that you make (preferably in p3k head, so I could
just pull them). But as I said: If the 'proper' behavior in 2.x was not
a deliberate workaround, there's really no need to change anything.
msg95066 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-11-09 10:49
Yes, I don't think Python 2.6 had a deliberate workaround.  I suspect
that it's just that one version of Python happened to use something like
0.0/0.0 to generate NaN, while another used some equivalent of
strtod("nan", ...).

I also remember noticing at some point that even on a single machine/OS,
the sign bit of 0.0/0.0 depends on which version of gcc and which
optimization flags are present.

So I think we're in agreement that there's no need to change anything
here;  I'll close this issue.

But:  I really *would* like to get the short float repr working with
suncc!  Issue #5792 is already open for this, so discussion should move
there.  (This is about much more than consistent nan signs: 
implementing short float repr gives a whole bunch of benefits: correctly
rounded float <-> string conversions (including all float formatting
operations), a correctly rounded 'round' function, a prettier float
repr, ...).
msg95067 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-11-09 11:31
Just to confirm the above:

In 2.6, PyFloat_FromString in Objects/floatobject.c ends up using the
system strtod to parse "nan" and "-nan" (except that if the system
strtod fails to recognise "nan" for some reason then it returns the
result of 0.0 * Infinity instead, and in that case disregards the sign).
 In 2.7 and 3.x, it ends up calling _Py_parse_inf_or_nan in
Python/pystrtod.c, and this returns 0.0 * Infinity for "nan" and -(0.0 *
Infinity) for "-nan".  And depending on compiler flags, 0.0 * Infinity
ends up being either +nan (this usually seems to happen when
optimization is on, so that the compiler itself evaluates 0.0 *
Infinity), or -nan (which happens when there's no optimization and the
FPU ends up doing the 0.0 * Infinity multiplication at runtime.) This
should explain the results you're seeing.
History
Date User Action Args
2009-11-09 11:31:37mark.dickinsonsetmessages: + msg95067
2009-11-09 10:49:29mark.dickinsonsetstatus: open -> closed
resolution: not a bug
messages: + msg95066
2009-11-08 13:49:12skrahsetmessages: + msg95041
2009-11-08 00:30:02mark.dickinsonsetmessages: + msg95033
2009-11-07 20:41:38skrahcreate