[Python-Dev] Mountain Lion drops sign of zero, breaks test_cmath...

Trent Nelson trent at snakebite.org
Fri Aug 17 10:00:50 CEST 2012


 The Mountain Lion build slave I set up earlier this evening fails on
 test_cmath:
 ======================================================================
 FAIL: test_specific_values (test.test_cmath.CMathTests)
 ----------------------------------------------------------------------
 Traceback (most recent call last):
 File "/Volumes/bay2/buildslave/cpython/2.7.snakebite-mountainlion-amd64/build/Lib/test/test_cmath.py", line 352, in test_specific_values
 msg=error_message)
 File "/Volumes/bay2/buildslave/cpython/2.7.snakebite-mountainlion-amd64/build/Lib/test/test_cmath.py", line 94, in rAssertAlmostEqual
 'got {!r}'.format(a, b))
 AssertionError: atan0000: atan(complex(0.0, 0.0))
 Expected: complex(0.0, 0.0)
 Received: complex(0.0, -0.0)
 Received value insufficiently close to expected value.
 Mountain Lion's atan/log1p appear to drop the negative sign when
 passed in -0.0, whereas previous versions of OS X didn't:
 Mountain Lion:
 % ~/log1p-viper 
 log1p_drops_zero_sign_test:
 atan2(log1p(-0.), -1.) != atan2(-0., -1.)
 3.14159 vs -3.14159
 atan_drops_zero_sign_test:
 atan2(-0., 0.): -0.00000
 atan2( 0., -0.): 3.14159
 atan2(-0., -0.): -3.14159
 atan2( 0., 0.): 0.00000
 log1p(-0.): 0.00000
 log1p( 0.): 0.00000
 Lion:
 % ./log1p
 log1p_drops_zero_sign_test:
 atan2(log1p(-0.), -1.) == atan2(-0., -1.)
 -3.14159 vs -3.14159
 atan_drops_zero_sign_test:
 atan2(-0., 0.): -0.00000
 atan2( 0., -0.): 3.14159
 atan2(-0., -0.): -3.14159
 atan2( 0., 0.): 0.00000
 log1p(-0.): -0.00000
 log1p( 0.): 0.00000
 (The C code for that is below.)
 configure.ac already has a test for this (it makes mention of AIX
 having similar behaviour), and the corresponding sysconfig entry
 named 'LOG1P_DROPS_ZERO_SIGN' is already being used on a few tests,
 i.e.:
 # The algorithm used for atan and atanh makes use of the system
 # log1p function; If that system function doesn't respect the sign
 # of zero, then atan and atanh will also have difficulties with
 # the sign of complex zeros.
 @requires_IEEE_754
 @unittest.skipIf(sysconfig.get_config_var('LOG1P_DROPS_ZERO_SIGN'),
 "system log1p() function doesn't preserve the sign")
 def testAtanSign(self):
 for z in complex_zeros:
 self.assertComplexIdentical(cmath.atan(z), z)
 @requires_IEEE_754
 @unittest.skipIf(sysconfig.get_config_var('LOG1P_DROPS_ZERO_SIGN'),
 "system log1p() function doesn't preserve the sign")
 def testAtanhSign(self):
 for z in complex_zeros:
 self.assertComplexIdentical(cmath.atanh(z), z)
 Taking a look at cmath_testcases.txt, and we can see this:
 -- These are tested in testAtanSign in test_cmath.py
 -- atan0000 atan 0.0 0.0 -> 0.0 0.0
 -- atan0001 atan 0.0 -0.0 -> 0.0 -0.0
 -- atan0002 atan -0.0 0.0 -> -0.0 0.0
 -- atan0003 atan -0.0 -0.0 -> -0.0 -0.0
 However, a few lines down, those tests crop up again:
 -- special values
 atan1000 atan -0.0 0.0 -> -0.0 0.0
 <snip>
 atan1014 atan 0.0 0.0 -> 0.0 0.0
 ....which is what causes the current test failures. I hacked
 test_cmath.py a bit to spit out all the errors it finds after
 it's finished parsing the test file (instead of bombing out on
 the first one), and it yielded this:
 FAIL: test_specific_values (test.test_cmath.CMathTests)
 ----------------------------------------------------------------------
 Traceback (most recent call last):
 File "/Volumes/bay2/buildslave/cpython/3.2.snakebite-mountainlion-amd64/build/Lib/test/test_cmath.py", line 446, in test_specific_values
 self.fail("\n".join(failures))
 AssertionError: atan1000: atan(complex(-0.0, 0.0))
 Expected: complex(-0.0, 0.0)
 Received: complex(-0.0, -0.0)
 Received value insufficiently close to expected value.
 atan1014: atan(complex(0.0, 0.0))
 Expected: complex(0.0, 0.0)
 Received: complex(0.0, -0.0)
 Received value insufficiently close to expected value.
 atanh0225: atanh(complex(-0.0, 5.6067e-320))
 Expected: complex(-0.0, 5.6067e-320)
 Received: complex(0.0, 5.6067e-320)
 Received value insufficiently close to expected value.
 atanh0227: atanh(complex(-0.0, -3.0861101e-316))
 Expected: complex(-0.0, -3.0861101e-316)
 Received: complex(0.0, -3.0861101e-316)
 Received value insufficiently close to expected value.
 atanh1024: atanh(complex(-0.0, -0.0))
 Expected: complex(-0.0, -0.0)
 Received: complex(0.0, -0.0)
 Received value insufficiently close to expected value.
 atanh1034: atanh(complex(-0.0, 0.0))
 Expected: complex(-0.0, 0.0)
 Received: complex(0.0, 0.0)
 Received value insufficiently close to expected value.
 This is the patch I came up with against test_cmath.py:
xenon% hg diff Lib/test/test_cmath.py
diff -r ce49599b9fdf Lib/test/test_cmath.py
--- a/Lib/test/test_cmath.py Thu Aug 16 22:14:43 2012 +0200
+++ b/Lib/test/test_cmath.py Fri Aug 17 07:54:05 2012 +0000
@@ -121,8 +121,10 @@
 # if both a and b are zero, check whether they have the same sign
 # (in theory there are examples where it would be legitimate for a
 # and b to have opposite signs; in practice these hardly ever
- # occur).
- if not a and not b:
+ # occur) -- the exception to this is if we're on a system that drops
+ # the sign on zeros.
+ drops_zero_sign = sysconfig.get_config_var('LOG1P_DROPS_ZERO_SIGN')
+ if not drops_zero_sign and not a and not b:
 if math.copysign(1., a) != math.copysign(1., b):
 self.fail(msg or 'zero has wrong sign: expected {!r}, '
 'got {!r}'.format(a, b))
 With that applied, all the test_cmath tests pass again (without any
 changes to the test file).
 Thoughts?
 Trent.
--
 C code for the example earlier:
#include <math.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
 printf("\nlog1p_drops_zero_sign_test:\n");
 if (atan2(log1p(-0.), -1.) == atan2(-0., -1.))
 printf(" atan2(log1p(-0.), -1.) == atan2(-0., -1.)\n");
 else
 printf(" atan2(log1p(-0.), -1.) != atan2(-0., -1.)\n");
 printf(
 " %.5f vs %.5f\n",
 atan2(log1p(-0.), -1.),
 atan2(-0., -1.)
 );
 printf("\natan_drops_zero_sign_test:\n");
 printf(" atan2(-0., 0.): %0.5f\n", atan2(-0., 0.));
 printf(" atan2( 0., -0.): %0.5f\n", atan2( 0., -0.));
 printf(" atan2(-0., -0.): %0.5f\n", atan2(-0., -0.));
 printf(" atan2( 0., 0.): %0.5f\n", atan2( 0., 0.));
 printf(" log1p(-0.): %0.5f\n", log1p(-0.));
 printf(" log1p( 0.): %0.5f\n", log1p( 0.));
}
/* vim:set ts=8 sw=4 sts=4 tw=78 et: */


More information about the Python-Dev mailing list

AltStyle によって変換されたページ (->オリジナル) /