Use the expression (x+0.0)-(y+0.0) instead of x+y when mixing NaN arg(s).
This uses 2 tricks to improve consistency so that more serious problems aren't hidden in simple regression tests by noise for the NaNs: - for a signaling NaN, adding 0.0 generates the invalid exception and converts to a quiet NaN, and doesn't have too many effects for other types of args (it converts -0 to +0 in some rounding modes, but that hopefully doesn't change the result after adding the NaN arg). This avoids some inconsistencies on i386 and ia64. On these arches, the result of an operation on 2 NaNs is apparently the largest or the smallest of the NaNs as bits (consistently largest or smallest for each arch, but the opposite). I forget which way the comparison goes and if the sign bit affects it. The quiet bit is is handled poorly by not always setting it before the comparision or ignoring it. Thus if one of the args was originally a signaling NaN and the other was originally a quiet NaN, then the result depends too much on whether the signaling NaN has been quieted at this point, which in turn depends on optimizations and promotions. E.g., passing float signaling NaNs to double functions must quiet them on conversion; on i387, loading a signaling NaN of type float or double (but not long double) into a register involves a conversion, so it quiets signaling NaNs, so if the addition has 2 register operands than it only sees quiet NaNs, but if the addition has a memory operand then it sees a signaling NaN iff it is in the memory operand. - subtraction instead of addition is used to avoid a dubious optimization in old versions of gcc. For SSE operations, mixing of NaNs apparently always gives the target operand. This is not as good as the i387 and ia64 behaviour. It doesn't mix NaNs at all, and makes addition not quite commutative. Old versions of gcc sometimes rewrite x+y to y+x and thus give different results (in bits) for NaNs. gcc-3.3.3 rewrites x+y to y+x for one of pow() and powf() but not the other, so starting from float NaN args x and y, powf(x, y) was almost always different from pow(x, y). These tricks won't give consistency of 2-arg float and double functions with long double ones on amd64, since long double ones use the i387 which has different semantics from SSE. Convert to __FBSDID().
This commit is contained in:
parent
9fb59f5567
commit
011cbae1fe
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=176266
@ -9,9 +9,8 @@
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[] = "$FreeBSD$";
|
||||
#endif
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/* __ieee754_pow(x,y) return x**y
|
||||
*
|
||||
@ -110,10 +109,10 @@ __ieee754_pow(double x, double y)
|
||||
/* y==zero: x**0 = 1 */
|
||||
if((iy|ly)==0) return one;
|
||||
|
||||
/* +-NaN return x+y */
|
||||
/* y!=zero: result is NaN if either arg is NaN */
|
||||
if(ix > 0x7ff00000 || ((ix==0x7ff00000)&&(lx!=0)) ||
|
||||
iy > 0x7ff00000 || ((iy==0x7ff00000)&&(ly!=0)))
|
||||
return x+y;
|
||||
return (x+0.0)+(y+0.0);
|
||||
|
||||
/* determine if y is an odd int when x < 0
|
||||
* yisint = 0 ... y is not an integer
|
||||
|
@ -13,9 +13,8 @@
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char rcsid[] = "$FreeBSD$";
|
||||
#endif
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "math.h"
|
||||
#include "math_private.h"
|
||||
@ -68,10 +67,10 @@ __ieee754_powf(float x, float y)
|
||||
/* y==zero: x**0 = 1 */
|
||||
if(iy==0) return one;
|
||||
|
||||
/* +-NaN return x+y */
|
||||
/* y!=zero: result is NaN if either arg is NaN */
|
||||
if(ix > 0x7f800000 ||
|
||||
iy > 0x7f800000)
|
||||
return x+y;
|
||||
return (x+0.0F)+(y+0.0F);
|
||||
|
||||
/* determine if y is an odd int when x < 0
|
||||
* yisint = 0 ... y is not an integer
|
||||
|
Loading…
Reference in New Issue
Block a user