diff options
author | Joseph Myers <joseph@codesourcery.com> | 2016-12-06 00:33:19 +0000 |
---|---|---|
committer | Joseph Myers <joseph@codesourcery.com> | 2016-12-06 00:33:19 +0000 |
commit | a91fd168a0db38563528dab1a13180fda2a5040c (patch) | |
tree | 9635f8f07429b3509688928500d91de84939529d | |
parent | ca6e601a9d4a72b3699cca15bad12ac1716bf49a (diff) | |
download | glibc-a91fd168a0db38563528dab1a13180fda2a5040c.tar.gz glibc-a91fd168a0db38563528dab1a13180fda2a5040c.tar.xz glibc-a91fd168a0db38563528dab1a13180fda2a5040c.zip |
Fix x86_64/x86 powl handling of sNaN arguments (bug 20916).
The x86_64/x86 powl implementations mishandle sNaN arguments, both by returning sNaN in some cases (instead of doing arithmetic on the arguments to produce the result when NaN arguments result in NaN results) and by treating sNaN the same as qNaN for arguments (1, sNaN) and (sNaN, 0), contrary to TS 18661-1 which requires those cases to return qNaN instead of 1. This patch makes the x86_64/x86 powl implementations follow TS 18661-1 semantics for sNaN arguments; sNaN tests are also added for pow. Given the problems with testing float and double sNaN arguments on 32-bit x86 (sNaN tests disabled because the compiler may convert unnecessarily to a qNaN when passing arguments), no changes are made to the powf and pow implementations there. Tested for x86_64 and x86. [BZ #20916] * sysdeps/i386/fpu/e_powl.S (__ieee754_powl): Do not return 1 for arguments (sNaN, 0) or (1, sNaN). Do arithmetic on NaN arguments to compute result. * sysdeps/x86_64/fpu/e_powl.S (__ieee754_powl): Likewise. * math/libm-test.inc (pow_test_data): Add tests of sNaN arguments.
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | math/libm-test.inc | 54 | ||||
-rw-r--r-- | sysdeps/i386/fpu/e_powl.S | 29 | ||||
-rw-r--r-- | sysdeps/x86_64/fpu/e_powl.S | 27 |
4 files changed, 109 insertions, 10 deletions
diff --git a/ChangeLog b/ChangeLog index 9c0aaa6e43..ab723ebfbb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2016-12-06 Joseph Myers <joseph@codesourcery.com> + + [BZ #20916] + * sysdeps/i386/fpu/e_powl.S (__ieee754_powl): Do not return 1 for + arguments (sNaN, 0) or (1, sNaN). Do arithmetic on NaN arguments + to compute result. + * sysdeps/x86_64/fpu/e_powl.S (__ieee754_powl): Likewise. + * math/libm-test.inc (pow_test_data): Add tests of sNaN arguments. + 2016-12-05 Torvald Riegel <triegel@redhat.com> * include/atomic.h (__atomic_check_size_ls): New. diff --git a/math/libm-test.inc b/math/libm-test.inc index 85df9deb86..9123dcfc48 100644 --- a/math/libm-test.inc +++ b/math/libm-test.inc @@ -11090,6 +11090,10 @@ static const struct test_ff_f_data pow_test_data[] = TEST_ff_f (pow, -qnan_value, 0, 1, ERRNO_UNCHANGED|NO_TEST_MATHVEC), TEST_ff_f (pow, qnan_value, minus_zero, 1, ERRNO_UNCHANGED|NO_TEST_MATHVEC), TEST_ff_f (pow, -qnan_value, minus_zero, 1, ERRNO_UNCHANGED|NO_TEST_MATHVEC), + TEST_ff_f (pow, snan_value, 0, qnan_value, INVALID_EXCEPTION|NO_TEST_MATHVEC), + TEST_ff_f (pow, -snan_value, 0, qnan_value, INVALID_EXCEPTION|NO_TEST_MATHVEC), + TEST_ff_f (pow, snan_value, minus_zero, qnan_value, INVALID_EXCEPTION|NO_TEST_MATHVEC), + TEST_ff_f (pow, -snan_value, minus_zero, qnan_value, INVALID_EXCEPTION|NO_TEST_MATHVEC), TEST_ff_f (pow, 1.1L, plus_infty, plus_infty, ERRNO_UNCHANGED|NO_TEST_INLINE), TEST_ff_f (pow, plus_infty, plus_infty, plus_infty, ERRNO_UNCHANGED|NO_TEST_INLINE), @@ -11151,18 +11155,40 @@ static const struct test_ff_f_data pow_test_data[] = TEST_ff_f (pow, qnan_value, -qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, -qnan_value, qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, -qnan_value, -qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), + TEST_ff_f (pow, qnan_value, snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, qnan_value, -snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -qnan_value, snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -qnan_value, -snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, snan_value, qnan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, snan_value, -qnan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, qnan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, -qnan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, snan_value, snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, snan_value, -snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, -snan_value, qnan_value, INVALID_EXCEPTION), TEST_ff_f (pow, 0, qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, 0, -qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), + TEST_ff_f (pow, 0, snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, 0, -snan_value, qnan_value, INVALID_EXCEPTION), TEST_ff_f (pow, 1, qnan_value, 1, ERRNO_UNCHANGED), TEST_ff_f (pow, 1, -qnan_value, 1, ERRNO_UNCHANGED), + TEST_ff_f (pow, 1, snan_value, qnan_value, INVALID_EXCEPTION|NO_TEST_MATHVEC), + TEST_ff_f (pow, 1, -snan_value, qnan_value, INVALID_EXCEPTION|NO_TEST_MATHVEC), TEST_ff_f (pow, -1, qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, -1, -qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), + TEST_ff_f (pow, -1, snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -1, -snan_value, qnan_value, INVALID_EXCEPTION), TEST_ff_f (pow, qnan_value, 1, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, -qnan_value, 1, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), + TEST_ff_f (pow, snan_value, 1, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, 1, qnan_value, INVALID_EXCEPTION), TEST_ff_f (pow, qnan_value, -1, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, -qnan_value, -1, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), + TEST_ff_f (pow, snan_value, -1, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, -1, qnan_value, INVALID_EXCEPTION), - /* pow (x, qNaN) == qNaN. */ + /* pow (x, qNaN or sNaN) == qNaN. */ TEST_ff_f (pow, 3.0, qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, 3.0, -qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, minus_zero, qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), @@ -11173,6 +11199,16 @@ static const struct test_ff_f_data pow_test_data[] = TEST_ff_f (pow, -3.0, -qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, minus_infty, qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, minus_infty, -qnan_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), + TEST_ff_f (pow, 3.0, snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, 3.0, -snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, minus_zero, snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, minus_zero, -snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, plus_infty, snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, plus_infty, -snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -3.0, snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -3.0, -snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, minus_infty, snan_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, minus_infty, -snan_value, qnan_value, INVALID_EXCEPTION), TEST_ff_f (pow, qnan_value, 3.0, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, -qnan_value, 3.0, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), @@ -11190,6 +11226,22 @@ static const struct test_ff_f_data pow_test_data[] = TEST_ff_f (pow, -qnan_value, min_subnorm_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, qnan_value, -min_subnorm_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), TEST_ff_f (pow, -qnan_value, -min_subnorm_value, qnan_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED), + TEST_ff_f (pow, snan_value, 3.0, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, 3.0, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, snan_value, -3.0, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, -3.0, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, snan_value, plus_infty, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, plus_infty, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, snan_value, minus_infty, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, minus_infty, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, snan_value, 2.5, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, 2.5, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, snan_value, -2.5, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, -2.5, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, snan_value, min_subnorm_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, min_subnorm_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, snan_value, -min_subnorm_value, qnan_value, INVALID_EXCEPTION), + TEST_ff_f (pow, -snan_value, -min_subnorm_value, qnan_value, INVALID_EXCEPTION), TEST_ff_f (pow, 1, plus_infty, 1, ERRNO_UNCHANGED), TEST_ff_f (pow, -1, plus_infty, 1, ERRNO_UNCHANGED), diff --git a/sysdeps/i386/fpu/e_powl.S b/sysdeps/i386/fpu/e_powl.S index 923ee37222..57b80beb64 100644 --- a/sysdeps/i386/fpu/e_powl.S +++ b/sysdeps/i386/fpu/e_powl.S @@ -201,15 +201,21 @@ ENTRY(__ieee754_powl) fucomp %st(1) // x : y fnstsw sahf - je 31f - fxch // y : x -31: fstp %st(1) + je 33f +31: /* At least one argument NaN, and result should be NaN. */ + faddp + ret +33: jp 31b + /* pow (1, NaN); check if the NaN signaling. */ + testb $0x40, 23(%esp) + jz 31b + fstp %st(1) ret cfi_adjust_cfa_offset (8) 32: addl $8, %esp cfi_adjust_cfa_offset (-8) - fstp %st(1) + faddp ret cfi_adjust_cfa_offset (8) @@ -241,12 +247,24 @@ ENTRY(__ieee754_powl) cfi_adjust_cfa_offset (-36) ret - // pow(x,±0) = 1 + // pow(x,±0) = 1, unless x is sNaN .align ALIGNARG(4) 11: fstp %st(0) // pop y + fldt 4(%esp) // x + fxam + fnstsw + andb $0x45, %ah + cmpb $0x01, %ah + je 112f // x is NaN +111: fstp %st(0) fldl MO(one) ret +112: testb $0x40, 11(%esp) + jnz 111b + fadd %st(0) + ret + // y == ±inf .align ALIGNARG(4) 12: fstp %st(0) // pop y @@ -274,6 +292,7 @@ ENTRY(__ieee754_powl) .align ALIGNARG(4) 13: fldt 4(%esp) // load x == NaN + fadd %st(0) ret cfi_adjust_cfa_offset (8) diff --git a/sysdeps/x86_64/fpu/e_powl.S b/sysdeps/x86_64/fpu/e_powl.S index 4a7f3a18d3..2b36077a32 100644 --- a/sysdeps/x86_64/fpu/e_powl.S +++ b/sysdeps/x86_64/fpu/e_powl.S @@ -184,9 +184,15 @@ ENTRY(__ieee754_powl) 30: fldt 8(%rsp) // x : y fldl MO(one) // 1.0 : x : y fucomip %st(1),%st // x : y - je 31f - fxch // y : x -31: fstp %st(1) + je 32f +31: /* At least one argument NaN, and result should be NaN. */ + faddp + ret +32: jc 31b + /* pow (1, NaN); check if the NaN signaling. */ + testb $0x40, 31(%rsp) + jz 31b + fstp %st(1) ret .align ALIGNARG(4) @@ -217,12 +223,24 @@ ENTRY(__ieee754_powl) cfi_adjust_cfa_offset (-40) ret - // pow(x,±0) = 1 + // pow(x,±0) = 1, unless x is sNaN .align ALIGNARG(4) 11: fstp %st(0) // pop y + fldt 8(%rsp) // x + fxam + fnstsw + andb $0x45, %ah + cmpb $0x01, %ah + je 112f // x is NaN +111: fstp %st(0) fldl MO(one) ret +112: testb $0x40, 15(%rsp) + jnz 111b + fadd %st(0) + ret + // y == ±inf .align ALIGNARG(4) 12: fstp %st(0) // pop y @@ -255,6 +273,7 @@ ENTRY(__ieee754_powl) .align ALIGNARG(4) 13: fldt 8(%rsp) // load x == NaN + fadd %st(0) ret .align ALIGNARG(4) |