[Python-Dev] Mountain Lion drops sign of zero, breaks test_cmath... (original) (raw)

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

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