NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: lib/58742: Sign of Zero Issue with libc fma(): Positive Zero Returned Instead of Negative
The following reply was made to PR lib/58742; it has been noted by GNATS.
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
To: furkanonder%protonmail.com@localhost
Cc: gnats-bugs%NetBSD.org@localhost, netbsd-bugs%NetBSD.org@localhost
Subject: Re: lib/58742: Sign of Zero Issue with libc fma(): Positive Zero Returned Instead of Negative
Date: Sat, 12 Oct 2024 19:46:34 +0000
I started adding FreeBSD's fma(3) tests, and adapted this one to use
slightly less magic constants (so we can do the same for float and
long double):
volatile double a = ldexp(1 - DBL_EPSILON, (DBL_MIN_EXP - 1)/2);
volatile double b = ldexp(1 + DBL_EPSILON, (DBL_MIN_EXP - 1)/2);
volatile double c = -DBL_MIN;
That is,
a = 0x1.ffffffffffffep-512
b = 0x1.0000000000001p-511
c = -0x1p-1022
for which a*b + c is a very small number below zero, -0x1p-1126, much
closer to zero than to the smallest negative subnormal, -0x1p-1074.
Stepping through fma(3) with gdb reveals that for these inputs, we end
up at:
268 if (r.hi == 0.0) {
269 /*
270 * When the addends cancel to 0, ensure that the result has
271 * the correct sign.
272 */
273 fesetround(oround);
274 {
275 volatile double vzs = zs; /* XXX gcc CSE bug workaround */
276 return (xy.hi + vzs + ldexp(xy.lo, spread));
277 }
278 }
https://nxr.netbsd.org/xref/src/lib/libm/src/s_fma.c?r=1.7#268
The local variables at this point are [hexadecimal notation added]:
(gdb) info locals
xs = 0.99999999999999978 [0x1.ffffffffffffep-1]
ys = 0.50000000000000011 [0x1.0000000000001p-1]
zs = -0.5
adj = <optimized out>
xy = {hi = 0.5, lo = -2.4651903288156619e-32 [-0x1p-105]}
r = {hi = 0, lo = 0}
oround = 0
ex = -511
ey = -510
ez = -1021
spread = -1021
Summing xy.hi + zs yields 0 because xy.hi = zs, and scaling
ldexp(xy.lo, spread) is rounded to zero, so it's zero plus zero plus
zero. The trouble is that the true sum (without rounding in ldexp)
isn't actually zero, so we're not rounding correctly to plus or minus
zero -- or, in non-default rounding modes like FE_DOWNWARD, away from
zero.
It's tempting, in this case, to try something like copysign(0, xy.lo)
or, to handle rounding modes other than FE_TONEAREST, something like
copysign(DBL_DENORM_MIN, xy.lo)/2, but that breaks some other cases.
Home |
Main Index |
Thread Index |
Old Index