Trent Nelson added the comment: I ran into this last night and posted to python-dev before realizing this bug had been raised: http://mail.python.org/pipermail/python-dev/2012-August/121359.html
I'll reproduce it here as I made a few observations that haven't yet been mentioned in this issue: 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: */ After thinking about it a bit longer, I think patching rAssertAlmostEqual is unnecessarily hacky. The problem is that the 'special value' atan tests in the cmath test file shouldn't have any zero tests -- these are handled in testAtanSign, which is what cmath_testcase.txt states: --------------------------- -- atan: Inverse tangent -- --------------------------- -- zeros -- 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 So, given, that, let's review the affected tests further down in cmath_testcase.txt: 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. I propose we remove the faulty zero tests as follows: % hg diff diff -r 05274ab06182 Lib/test/cmath_testcases.txt --- a/Lib/test/cmath_testcases.txt Sat May 05 21:57:17 2012 -0700 +++ b/Lib/test/cmath_testcases.txt Fri Aug 17 18:58:03 2012 +0000 @@ -849,7 +849,6 @@ atan0304 atan -9.9998886718268301e-321 -1.0 -> -0.78539816339744828 -368.76019403576692 -- special values -atan1000 atan -0.0 0.0 -> -0.0 0.0 atan1001 atan nan 0.0 -> nan 0.0 atan1002 atan -0.0 1.0 -> -0.0 inf divide-by-zero atan1003 atan -inf 0.0 -> -1.5707963267948966 0.0 @@ -863,7 +862,6 @@ atan1011 atan -2.2999999999999998 nan -> nan nan atan1012 atan -inf nan -> -1.5707963267948966 0.0 ignore-imag-sign atan1013 atan nan nan -> nan nan -atan1014 atan 0.0 0.0 -> 0.0 0.0 atan1015 atan 0.0 1.0 -> 0.0 inf divide-by-zero atan1016 atan inf 0.0 -> 1.5707963267948966 0.0 atan1017 atan inf 2.2999999999999998 -> 1.5707963267948966 0.0 @@ -873,7 +871,6 @@ atan1021 atan 0.0 nan -> nan nan atan1022 atan 2.2999999999999998 nan -> nan nan atan1023 atan inf nan -> 1.5707963267948966 0.0 ignore-imag-sign -atan1024 atan 0.0 -0.0 -> 0.0 -0.0 atan1025 atan nan -0.0 -> nan -0.0 atan1026 atan 0.0 -1.0 -> 0.0 -inf divide-by-zero atan1027 atan inf -0.0 -> 1.5707963267948966 -0.0 @@ -883,7 +880,6 @@ atan1031 atan 2.2999999999999998 -inf -> 1.5707963267948966 -0.0 atan1032 atan inf -inf -> 1.5707963267948966 -0.0 atan1033 atan nan -inf -> nan -0.0 -atan1034 atan -0.0 -0.0 -> -0.0 -0.0 atan1035 atan -0.0 -1.0 -> -0.0 -inf divide-by-zero atan1036 atan -inf -0.0 -> -1.5707963267948966 -0.0 atan1037 atan -inf -2.2999999999999998 -> -1.5707963267948966 -0.0 That leaves us with: 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. Those aren't duplicate tests like the 0.0 -> -0.0 ones, so, how should they be handled? The proposed patch to rAssertAlmostEqual will skip these ones, but perhaps something like this would be more appropriate: diff -r 12c062dbe746 Lib/test/cmath_testcases.txt --- a/Lib/test/cmath_testcases.txt Thu Aug 16 22:18:37 2012 +0200 +++ b/Lib/test/cmath_testcases.txt Fri Aug 17 15:37:11 2012 -0400 @@ -849,7 +849,6 @@ atan0304 atan -9.9998886718268301e-321 -1.0 -> -0.78539816339744828 -368.76019403576692 -- special values -atan1000 atan -0.0 0.0 -> -0.0 0.0 atan1001 atan nan 0.0 -> nan 0.0 atan1002 atan -0.0 1.0 -> -0.0 inf divide-by-zero atan1003 atan -inf 0.0 -> -1.5707963267948966 0.0 @@ -863,7 +862,6 @@ atan1011 atan -2.2999999999999998 nan -> nan nan atan1012 atan -inf nan -> -1.5707963267948966 0.0 ignore-imag-sign atan1013 atan nan nan -> nan nan -atan1014 atan 0.0 0.0 -> 0.0 0.0 atan1015 atan 0.0 1.0 -> 0.0 inf divide-by-zero atan1016 atan inf 0.0 -> 1.5707963267948966 0.0 atan1017 atan inf 2.2999999999999998 -> 1.5707963267948966 0.0 @@ -873,7 +871,6 @@ atan1021 atan 0.0 nan -> nan nan atan1022 atan 2.2999999999999998 nan -> nan nan atan1023 atan inf nan -> 1.5707963267948966 0.0 ignore-imag-sign -atan1024 atan 0.0 -0.0 -> 0.0 -0.0 atan1025 atan nan -0.0 -> nan -0.0 atan1026 atan 0.0 -1.0 -> 0.0 -inf divide-by-zero atan1027 atan inf -0.0 -> 1.5707963267948966 -0.0 @@ -883,7 +880,6 @@ atan1031 atan 2.2999999999999998 -inf -> 1.5707963267948966 -0.0 atan1032 atan inf -inf -> 1.5707963267948966 -0.0 atan1033 atan nan -inf -> nan -0.0 -atan1034 atan -0.0 -0.0 -> -0.0 -0.0 atan1035 atan -0.0 -1.0 -> -0.0 -inf divide-by-zero atan1036 atan -inf -0.0 -> -1.5707963267948966 -0.0 atan1037 atan -inf -2.2999999999999998 -> -1.5707963267948966 -0.0 @@ -1011,9 +1007,7 @@ atanh0222 atanh -5.9656816081325078e-317 9.9692253555416263e-313 -> -5.9656816081325078e-317 9.9692253555416263e-313 atanh0223 atanh -6.5606671178400239e-313 -2.1680936406357335e-309 -> -6.5606671178400239e-313 -2.1680936406357335e-309 atanh0224 atanh 0.0 2.5230944401820779e-319 -> 0.0 2.5230944401820779e-319 -atanh0225 atanh -0.0 5.6066569490064658e-320 -> -0.0 5.6066569490064658e-320 atanh0226 atanh 0.0 -2.4222487249468377e-317 -> 0.0 -2.4222487249468377e-317 -atanh0227 atanh -0.0 -3.0861101089206037e-316 -> -0.0 -3.0861101089206037e-316 atanh0228 atanh 3.1219222884393986e-310 0.0 -> 3.1219222884393986e-310 0.0 atanh0229 atanh 9.8926337564976196e-309 -0.0 -> 9.8926337564976196e-309 -0.0 atanh0230 atanh -1.5462535092918154e-312 0.0 -> -1.5462535092918154e-312 0.0 @@ -1051,7 +1045,6 @@ atanh1021 atanh nan -0.0 -> nan nan atanh1022 atanh nan -2.3 -> nan nan atanh1023 atanh nan -inf -> 0.0 -1.5707963267948966 ignore-real-sign -atanh1024 atanh -0.0 -0.0 -> -0.0 -0.0 atanh1025 atanh -0.0 nan -> -0.0 nan atanh1026 atanh -1.0 -0.0 -> -inf -0.0 divide-by-zero atanh1027 atanh -0.0 -inf -> -0.0 -1.5707963267948966 @@ -1061,7 +1054,6 @@ atanh1031 atanh -inf -2.3 -> -0.0 -1.5707963267948966 atanh1032 atanh -inf -inf -> -0.0 -1.5707963267948966 atanh1033 atanh -inf nan -> -0.0 nan -atanh1034 atanh -0.0 0.0 -> -0.0 0.0 atanh1035 atanh -1.0 0.0 -> -inf 0.0 divide-by-zero atanh1036 atanh -0.0 inf -> -0.0 1.5707963267948966 atanh1037 atanh -2.3 inf -> -0.0 1.5707963267948966 diff -r 12c062dbe746 Lib/test/test_cmath.py --- a/Lib/test/test_cmath.py Thu Aug 16 22:18:37 2012 +0200 +++ b/Lib/test/test_cmath.py Fri Aug 17 15:37:11 2012 -0400 @@ -532,6 +532,36 @@ for z in complex_zeros: self.assertComplexIdentical(cmath.atanh(z), z) + @requires_IEEE_754 + @unittest.skipIf(sysconfig.get_config_var('LOG1P_DROPS_ZERO_SIGN'), + "system log1p() function doesn't preserve the sign") + def testAtanhSignNearZero(self): + for z in (-30861101e-316, 5.6067e-320): + expected = complex(-0.0, z) + actual = cmath.atanh(complex(-0.0, z)) + error_message = ( + 'Expected: complex({!r}, {!r})\n' + 'Received: complex({!r}, {!r})\n' + 'Received value insufficiently close to expected value.' + ).format( + expected.real, + expected.imag, + actual.real, + actual.imag + ) + + self.rAssertAlmostEqual( + expected.real, + actual.real, + abs_err=5e-323, + msg=error_message, + ) + + self.rAssertAlmostEqual( + expected.imag, + actual.imag, + msg=error_message, + ) def test_main(): run_unittest(CMathTests) With this patch applied, test_cmath passes on both Mountain Lion and Lion. I've attached this patch as issue15477.patch. Thoughts? ---------- keywords: +patch nosy: +trent Added file: http://bugs.python.org/file26880/issue15477.patch _______________________________________ Python tracker <rep...@bugs.python.org> <http://bugs.python.org/issue15477> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com