mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-23 05:20:06 +00:00
600fa36158
ISO C requires the result of nextafter to be independent of the rounding mode, even when underflow or overflow occurs. This patch fixes the bug in various nextafter implementations that, having done an overflowing computation to force an overflow exception (correct), they then return the result of that computation rather than an infinity computed some other way (incorrect, when the overflowing result of arithmetic with that sign and rounding mode is finite but the correct result is infinite) - generally by falling through to existing code to return a value that in fact is correct for this case (but was computed by an integer increment and so without generating the exceptions required). Having fixed the bug, the previously deferred conversion of nextafter testing in libm-test.inc to ALL_RM_TEST is also included. Tested x86_64 and x86; also spot-checked results of nextafter tests for powerpc32 and mips64 to test the ldbl-128ibm and ldbl-128 changes. (The m68k change is untested.) [BZ #16677] * math/s_nextafter.c (__nextafter): Do not return value from overflowing computation. * sysdeps/i386/fpu/s_nextafterl.c (__nextafterl): Likewise. * sysdeps/ieee754/flt-32/s_nextafterf.c (__nextafterf): Likewise. * sysdeps/ieee754/ldbl-128/s_nextafterl.c (__nextafterl): Likewise. * sysdeps/ieee754/ldbl-128ibm/s_nextafterl.c (__nextafterl): Likewise. * sysdeps/m68k/m680x0/fpu/s_nextafterl.c (__nextafterl): Likewise. * math/libm-test.inc (nextafter_test): Use ALL_RM_TEST.
123 lines
2.9 KiB
C
123 lines
2.9 KiB
C
/* s_nextafterl.c -- long double version of s_nextafter.c.
|
|
* Special version for i387.
|
|
* Conversion to long double by Ulrich Drepper,
|
|
* Cygnus Support, drepper@cygnus.com.
|
|
*/
|
|
|
|
/*
|
|
* ====================================================
|
|
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
|
*
|
|
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
|
* Permission to use, copy, modify, and distribute this
|
|
* software is freely granted, provided that this notice
|
|
* is preserved.
|
|
* ====================================================
|
|
*/
|
|
|
|
#if defined(LIBM_SCCS) && !defined(lint)
|
|
static char rcsid[] = "$NetBSD: $";
|
|
#endif
|
|
|
|
/* IEEE functions
|
|
* nextafterl(x,y)
|
|
* return the next machine floating-point number of x in the
|
|
* direction toward y.
|
|
* Special cases:
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <math_private.h>
|
|
|
|
long double __nextafterl(long double x, long double y)
|
|
{
|
|
u_int32_t hx,hy,ix,iy;
|
|
u_int32_t lx,ly;
|
|
int32_t esx,esy;
|
|
|
|
GET_LDOUBLE_WORDS(esx,hx,lx,x);
|
|
GET_LDOUBLE_WORDS(esy,hy,ly,y);
|
|
ix = esx&0x7fff; /* |x| */
|
|
iy = esy&0x7fff; /* |y| */
|
|
|
|
/* Intel's extended format has the normally implicit 1 explicit
|
|
present. Sigh! */
|
|
if(((ix==0x7fff)&&(((hx&0x7fffffff)|lx)!=0)) || /* x is nan */
|
|
((iy==0x7fff)&&(((hy&0x7fffffff)|ly)!=0))) /* y is nan */
|
|
return x+y;
|
|
if(x==y) return y; /* x=y, return y */
|
|
if((ix|hx|lx)==0) { /* x == 0 */
|
|
long double u;
|
|
SET_LDOUBLE_WORDS(x,esy&0x8000,0,1);/* return +-minsubnormal */
|
|
u = math_opt_barrier (x);
|
|
u = u * u;
|
|
math_force_eval (u); /* raise underflow flag */
|
|
return x;
|
|
}
|
|
if(esx>=0) { /* x > 0 */
|
|
if(esx>esy||((esx==esy) && (hx>hy||((hx==hy)&&(lx>ly))))) {
|
|
/* x > y, x -= ulp */
|
|
if(lx==0) {
|
|
if (hx <= 0x80000000) {
|
|
if (esx == 0) {
|
|
--hx;
|
|
} else {
|
|
esx -= 1;
|
|
hx = hx - 1;
|
|
if (esx > 0)
|
|
hx |= 0x80000000;
|
|
}
|
|
} else
|
|
hx -= 1;
|
|
}
|
|
lx -= 1;
|
|
} else { /* x < y, x += ulp */
|
|
lx += 1;
|
|
if(lx==0) {
|
|
hx += 1;
|
|
if (hx==0 || (esx == 0 && hx == 0x80000000)) {
|
|
esx += 1;
|
|
hx |= 0x80000000;
|
|
}
|
|
}
|
|
}
|
|
} else { /* x < 0 */
|
|
if(esy>=0||(esx>esy||((esx==esy)&&(hx>hy||((hx==hy)&&(lx>ly)))))){
|
|
/* x < y, x -= ulp */
|
|
if(lx==0) {
|
|
if (hx <= 0x80000000) {
|
|
esx -= 1;
|
|
hx = hx - 1;
|
|
if ((esx&0x7fff) > 0)
|
|
hx |= 0x80000000;
|
|
} else
|
|
hx -= 1;
|
|
}
|
|
lx -= 1;
|
|
} else { /* x > y, x += ulp */
|
|
lx += 1;
|
|
if(lx==0) {
|
|
hx += 1;
|
|
if (hx==0 || (esx == 0xffff8000 && hx == 0x80000000)) {
|
|
esx += 1;
|
|
hx |= 0x80000000;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
esy = esx&0x7fff;
|
|
if(esy==0x7fff) {
|
|
long double u = x + x; /* overflow */
|
|
math_force_eval (u);
|
|
}
|
|
if(esy==0) {
|
|
long double u = x*x; /* underflow */
|
|
math_force_eval (u); /* raise underflow flag */
|
|
}
|
|
SET_LDOUBLE_WORDS(x,esx,hx,lx);
|
|
return x;
|
|
}
|
|
weak_alias (__nextafterl, nextafterl)
|
|
strong_alias (__nextafterl, __nexttowardl)
|
|
weak_alias (__nextafterl, nexttowardl)
|