math: rewrite rounding functions (ceil, floor, trunc, round, rint)
authorSzabolcs Nagy <nsz@port70.net>
Tue, 3 Sep 2013 03:27:02 +0000 (03:27 +0000)
committerSzabolcs Nagy <nsz@port70.net>
Thu, 5 Sep 2013 11:30:07 +0000 (11:30 +0000)
* faster, smaller, cleaner implementation than the bit hacks of fdlibm
* use arithmetics like y=(double)(x+0x1p52)-0x1p52, which is an integer
neighbor of x in all rounding modes (0<=x<0x1p52) and only use bithacks
when that's faster and smaller (for float it usually is)
* the code assumes standard excess precision handling for casts
* long double code supports both ld80 and ld128
* nearbyint is not changed (it is a wrapper around rint)

15 files changed:
src/math/ceil.c
src/math/ceilf.c
src/math/ceill.c
src/math/floor.c
src/math/floorf.c
src/math/floorl.c
src/math/rint.c
src/math/rintf.c
src/math/rintl.c
src/math/round.c
src/math/roundf.c
src/math/roundl.c
src/math/trunc.c
src/math/truncf.c
src/math/truncl.c

index 195551805dded45c289e6c2053eed83cd7908e76..22dc224c5f38b1fdebbdba03d36bf0f408298baa 100644 (file)
@@ -1,82 +1,24 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_ceil.c */
-/*
- * ====================================================
- * 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.
- * ====================================================
- */
-/*
- * ceil(x)
- * Return x rounded toward -inf to integral value
- * Method:
- *      Bit twiddling.
- * Exception:
- *      Inexact flag raised if x not equal to ceil(x).
- */
-
 #include "libm.h"
 
-static const double huge = 1.0e300;
-
 double ceil(double x)
 {
-       int32_t i0,i1,j0;
-       uint32_t i,j;
+       union {double f; uint64_t i;} u = {x};
+       int e = u.i >> 52 & 0x7ff;
+       double_t y;
 
-       EXTRACT_WORDS(i0, i1, x);
-       // FIXME signed shift
-       j0 = ((i0>>20)&0x7ff) - 0x3ff;
-       if (j0 < 20) {
-               if (j0 < 0) {
-                        /* raise inexact if x != 0 */
-                       if (huge+x > 0.0) {
-                               if (i0 < 0) {
-                                       i0 = 0x80000000;
-                                       i1=0;
-                               } else if ((i0|i1) != 0) {
-                                       i0=0x3ff00000;
-                                       i1=0;
-                               }
-                       }
-               } else {
-                       i = 0x000fffff>>j0;
-                       if (((i0&i)|i1) == 0) /* x is integral */
-                               return x;
-                       /* raise inexact flag */
-                       if (huge+x > 0.0) {
-                               if (i0 > 0)
-                                       i0 += 0x00100000>>j0;
-                               i0 &= ~i;
-                               i1 = 0;
-                       }
-               }
-       } else if (j0 > 51) {
-               if (j0 == 0x400)  /* inf or NaN */
-                       return x+x;
-               return x;         /* x is integral */
-       } else {
-               i = (uint32_t)0xffffffff>>(j0-20);
-               if ((i1&i) == 0)
-                       return x; /* x is integral */
-               /* raise inexact flag */
-               if (huge+x > 0.0) {
-                       if (i0 > 0) {
-                               if (j0 == 20)
-                                       i0 += 1;
-                               else {
-                                       j = i1 + (1<<(52-j0));
-                                       if (j < i1)  /* got a carry */
-                                               i0 += 1;
-                                       i1 = j;
-                               }
-                       }
-                       i1 &= ~i;
-               }
+       if (e >= 0x3ff+52 || x == 0)
+               return x;
+       /* y = int(x) - x, where int(x) is an integer neighbor of x */
+       if (u.i >> 63)
+               y = (double)(x - 0x1p52) + 0x1p52 - x;
+       else
+               y = (double)(x + 0x1p52) - 0x1p52 - x;
+       /* special case because of non-nearest rounding modes */
+       if (e <= 0x3ff-1) {
+               FORCE_EVAL(y);
+               return u.i >> 63 ? -0.0 : 1;
        }
-       INSERT_WORDS(x, i0, i1);
-       return x;
+       if (y < 0)
+               return x + y + 1;
+       return x + y;
 }
index fec945b6970e286d6148e040942de7dc143b45ac..869835f39783835b9c2fdda05c16d1e4e4ee6883 100644 (file)
@@ -1,54 +1,27 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_ceilf.c */
-/*
- * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@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.
- * ====================================================
- */
-
 #include "libm.h"
 
-static const float huge = 1.0e30;
-
 float ceilf(float x)
 {
-       int32_t i0,j0;
-       uint32_t i;
+       union {float f; uint32_t i;} u = {x};
+       int e = (int)(u.i >> 23 & 0xff) - 0x7f;
+       uint32_t m;
 
-       GET_FLOAT_WORD(i0, x);
-       j0 = ((i0>>23)&0xff) - 0x7f;
-       if (j0 < 23) {
-               if (j0 < 0) {
-                       /* raise inexact if x != 0 */
-                       if (huge+x > 0.0f) {
-                               if (i0 < 0)
-                                       i0 = 0x80000000;
-                               else if(i0 != 0)
-                                       i0 = 0x3f800000;
-                       }
-               } else {
-                       i = 0x007fffff>>j0;
-                       if ((i0&i) == 0)
-                               return x; /* x is integral */
-                       /* raise inexact flag */
-                       if (huge+x > 0.0f) {
-                               if (i0 > 0)
-                                       i0 += 0x00800000>>j0;
-                               i0 &= ~i;
-                       }
-               }
+       if (e >= 23)
+               return x;
+       if (e >= 0) {
+               m = 0x007fffff >> e;
+               if ((u.i & m) == 0)
+                       return x;
+               FORCE_EVAL(x + 0x1p120f);
+               if (u.i >> 31 == 0)
+                       u.i += m;
+               u.i &= ~m;
        } else {
-               if (j0 == 0x80)  /* inf or NaN */
-                       return x+x;
-               return x; /* x is integral */
+               FORCE_EVAL(x + 0x1p120f);
+               if (u.i >> 31)
+                       u.f = -0.0;
+               else if (u.i << 1)
+                       u.f = 1.0;
        }
-       SET_FLOAT_WORD(x, i0);
-       return x;
+       return u.f;
 }
index a3523f9d3a8258ea96ea862c38d2e91d83179383..a2cb0a7f999607e8b55a3582a040497618553c65 100644 (file)
@@ -1,23 +1,3 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_ceill.c */
-/*
- * ====================================================
- * 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.
- * ====================================================
- */
-/*
- * ceill(x)
- * Return x rounded toward -inf to integral value
- * Method:
- *      Bit twiddling.
- * Exception:
- *      Inexact flag raised if x not equal to ceill(x).
- */
-
 #include "libm.h"
 
 #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
@@ -26,77 +6,31 @@ long double ceill(long double x)
        return ceil(x);
 }
 #elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
-
-#ifdef LDBL_IMPLICIT_NBIT
-#define MANH_SIZE       (LDBL_MANH_SIZE + 1)
-#define INC_MANH(u, c)  do {                                    \
-       uint64_t o = u.bits.manh;                               \
-       u.bits.manh += (c);                                     \
-       if (u.bits.manh < o)                                    \
-               u.bits.exp++;                                   \
-} while (0)
-#else
-#define MANH_SIZE       LDBL_MANH_SIZE
-#define INC_MANH(u, c)  do {                                    \
-       uint64_t o = u.bits.manh;                               \
-       u.bits.manh += (c);                                     \
-       if (u.bits.manh < o) {                                  \
-               u.bits.exp++;                                   \
-               u.bits.manh |= 1llu << (LDBL_MANH_SIZE - 1);    \
-       }                                                       \
-} while (0)
+#if LDBL_MANT_DIG == 64
+#define TOINT 0x1p63
+#elif LDBL_MANT_DIG == 113
+#define TOINT 0x1p112
 #endif
-
-static const long double huge = 1.0e300;
-
 long double ceill(long double x)
 {
-       union IEEEl2bits u = { .e = x };
-       int e = u.bits.exp - LDBL_MAX_EXP + 1;
+       union ldshape u = {x};
+       int e = u.i.se & 0x7fff;
+       long double y;
 
-       if (e < MANH_SIZE - 1) {
-               if (e < 0) {
-                       /* raise inexact if x != 0 */
-                       if (huge + x > 0.0)
-                               if (u.bits.exp > 0 ||
-                                       (u.bits.manh | u.bits.manl) != 0)
-                                       u.e = u.bits.sign ? -0.0 : 1.0;
-               } else {
-                       uint64_t m = ((1llu << MANH_SIZE) - 1) >> (e + 1);
-                       if (((u.bits.manh & m) | u.bits.manl) == 0)
-                               return x;  /* x is integral */
-                       if (!u.bits.sign) {
-#ifdef LDBL_IMPLICIT_NBIT
-                               if (e == 0)
-                                       u.bits.exp++;
-                               else
-#endif
-                               INC_MANH(u, 1llu << (MANH_SIZE - e - 1));
-                       }
-                       /* raise inexact flag */
-                       if (huge + x > 0.0) {
-                               u.bits.manh &= ~m;
-                               u.bits.manl = 0;
-                       }
-               }
-       } else if (e < LDBL_MANT_DIG - 1) {
-               uint64_t m = (uint64_t)-1 >> (64 - LDBL_MANT_DIG + e + 1);
-               if ((u.bits.manl & m) == 0)
-                       return x;  /* x is integral */
-               if (!u.bits.sign) {
-                       if (e == MANH_SIZE - 1)
-                               INC_MANH(u, 1);
-                       else {
-                               uint64_t o = u.bits.manl;
-                               u.bits.manl += 1llu << (LDBL_MANT_DIG - e - 1);
-                               if (u.bits.manl < o)    /* got a carry */
-                                       INC_MANH(u, 1);
-                       }
-               }
-               /* raise inexact flag */
-               if (huge + x > 0.0)
-                       u.bits.manl &= ~m;
+       if (e >= 0x3fff+LDBL_MANT_DIG-1 || x == 0)
+               return x;
+       /* y = int(x) - x, where int(x) is an integer neighbor of x */
+       if (u.i.se >> 15)
+               y = x - TOINT + TOINT - x;
+       else
+               y = x + TOINT - TOINT - x;
+       /* special case because of non-nearest rounding modes */
+       if (e <= 0x3fff-1) {
+               FORCE_EVAL(y);
+               return u.i.se >> 15 ? -0.0 : 1;
        }
-       return u.e;
+       if (y < 0)
+               return x + y + 1;
+       return x + y;
 }
 #endif
index ecb9dde8b4a0749c21e24476000cfd90040483e1..ebc9fabe908223d54522c73546ac314df59922a8 100644 (file)
@@ -1,82 +1,24 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_floor.c */
-/*
- * ====================================================
- * 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.
- * ====================================================
- */
-/*
- * floor(x)
- * Return x rounded toward -inf to integral value
- * Method:
- *      Bit twiddling.
- * Exception:
- *      Inexact flag raised if x not equal to floor(x).
- */
-
 #include "libm.h"
 
-static const double huge = 1.0e300;
-
 double floor(double x)
 {
-       int32_t i0,i1,j0;
-       uint32_t i,j;
+       union {double f; uint64_t i;} u = {x};
+       int e = u.i >> 52 & 0x7ff;
+       double_t y;
 
-       EXTRACT_WORDS(i0, i1, x);
-       // FIXME: signed shift
-       j0 = ((i0>>20)&0x7ff) - 0x3ff;
-       if (j0 < 20) {
-               if (j0 < 0) {  /* |x| < 1 */
-                       /* raise inexact if x != 0 */
-                       if (huge+x > 0.0) {
-                               if (i0 >= 0) {  /* x >= 0 */
-                                       i0 = i1 = 0;
-                               } else if (((i0&0x7fffffff)|i1) != 0) {
-                                       i0 = 0xbff00000;
-                                       i1 = 0;
-                               }
-                       }
-               } else {
-                       i = 0x000fffff>>j0;
-                       if (((i0&i)|i1) == 0)
-                               return x; /* x is integral */
-                        /* raise inexact flag */
-                       if (huge+x > 0.0) {
-                               if (i0 < 0)
-                                       i0 += 0x00100000>>j0;
-                               i0 &= ~i;
-                               i1 = 0;
-                       }
-               }
-       } else if (j0 > 51) {
-               if (j0 == 0x400)
-                       return x+x; /* inf or NaN */
-               else
-                       return x;   /* x is integral */
-       } else {
-               i = (uint32_t)0xffffffff>>(j0-20);
-               if ((i1&i) == 0)
-                       return x;   /* x is integral */
-               /* raise inexact flag */
-               if (huge+x > 0.0) {
-                       if (i0 < 0) {
-                               if (j0 == 20)
-                                       i0++;
-                               else {
-                                       j = i1+(1<<(52-j0));
-                                       if (j < i1)
-                                               i0++; /* got a carry */
-                                       i1 = j;
-                               }
-                       }
-                       i1 &= ~i;
-               }
+       if (e >= 0x3ff+52 || x == 0)
+               return x;
+       /* y = int(x) - x, where int(x) is an integer neighbor of x */
+       if (u.i >> 63)
+               y = (double)(x - 0x1p52) + 0x1p52 - x;
+       else
+               y = (double)(x + 0x1p52) - 0x1p52 - x;
+       /* special case because of non-nearest rounding modes */
+       if (e <= 0x3ff-1) {
+               FORCE_EVAL(y);
+               return u.i >> 63 ? -1 : 0;
        }
-       INSERT_WORDS(x, i0, i1);
-       return x;
+       if (y > 0)
+               return x + y - 1;
+       return x + y;
 }
index 23fa5e7d56949feb6b3b4935da3e95a96362048e..dceec739dbef5c4039fd4a5c7cf5c95ac593777d 100644 (file)
@@ -1,64 +1,27 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_floorf.c */
-/*
- * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@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.
- * ====================================================
- */
-/*
- * floorf(x)
- * Return x rounded toward -inf to integral value
- * Method:
- *      Bit twiddling.
- * Exception:
- *      Inexact flag raised if x not equal to floorf(x).
- */
-
 #include "libm.h"
 
-static const float huge = 1.0e30;
-
 float floorf(float x)
 {
-       int32_t i0,j0;
-       uint32_t i;
+       union {float f; uint32_t i;} u = {x};
+       int e = (int)(u.i >> 23 & 0xff) - 0x7f;
+       uint32_t m;
 
-       GET_FLOAT_WORD(i0, x);
-       // FIXME: signed shift
-       j0 = ((i0>>23)&0xff) - 0x7f;
-       if (j0 < 23) {
-               if (j0 < 0) {  /* |x| < 1 */
-                       /* raise inexact if x != 0 */
-                       if (huge+x > 0.0f) {
-                               if (i0 >= 0)  /* x >= 0 */
-                                       i0 = 0;
-                               else if ((i0&0x7fffffff) != 0)
-                                       i0 = 0xbf800000;
-                       }
-               } else {
-                       i = 0x007fffff>>j0;
-                       if ((i0&i) == 0)
-                               return x; /* x is integral */
-                       /* raise inexact flag */
-                       if (huge+x > 0.0f) {
-                               if (i0 < 0)
-                                       i0 += 0x00800000>>j0;
-                               i0 &= ~i;
-                       }
-               }
+       if (e >= 23)
+               return x;
+       if (e >= 0) {
+               m = 0x007fffff >> e;
+               if ((u.i & m) == 0)
+                       return x;
+               FORCE_EVAL(x + 0x1p120f);
+               if (u.i >> 31)
+                       u.i += m;
+               u.i &= ~m;
        } else {
-               if (j0 == 0x80)  /* inf or NaN */
-                       return x+x;
-               else
-                       return x;  /* x is integral */
+               FORCE_EVAL(x + 0x1p120f);
+               if (u.i >> 31 == 0)
+                       u.i = 0;
+               else if (u.i << 1)
+                       u.f = -1.0;
        }
-       SET_FLOAT_WORD(x, i0);
-       return x;
+       return u.f;
 }
index 3901b060b2823613a1757900683577b9810c325e..961f9e890b7362655a1c24938cf02e61e21e3d75 100644 (file)
@@ -1,23 +1,3 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_floorl.c */
-/*
- * ====================================================
- * 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.
- * ====================================================
- */
-/*
- * floorl(x)
- * Return x rounded toward -inf to integral value
- * Method:
- *      Bit twiddling.
- * Exception:
- *      Inexact flag raised if x not equal to floorl(x).
- */
-
 #include "libm.h"
 
 #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
@@ -26,77 +6,31 @@ long double floorl(long double x)
        return floor(x);
 }
 #elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
-
-#ifdef LDBL_IMPLICIT_NBIT
-#define MANH_SIZE       (LDBL_MANH_SIZE + 1)
-#define INC_MANH(u, c)  do {                                    \
-       uint64_t o = u.bits.manh;                               \
-       u.bits.manh += (c);                                     \
-       if (u.bits.manh < o)                                    \
-               u.bits.exp++;                                   \
-} while (0)
-#else
-#define MANH_SIZE       LDBL_MANH_SIZE
-#define INC_MANH(u, c)  do {                                    \
-       uint64_t o = u.bits.manh;                               \
-       u.bits.manh += (c);                                     \
-       if (u.bits.manh < o) {                                  \
-               u.bits.exp++;                                   \
-               u.bits.manh |= 1llu << (LDBL_MANH_SIZE - 1);    \
-       }                                                       \
-} while (0)
+#if LDBL_MANT_DIG == 64
+#define TOINT 0x1p63
+#elif LDBL_MANT_DIG == 113
+#define TOINT 0x1p112
 #endif
-
-static const long double huge = 1.0e300;
-
 long double floorl(long double x)
 {
-       union IEEEl2bits u = { .e = x };
-       int e = u.bits.exp - LDBL_MAX_EXP + 1;
+       union ldshape u = {x};
+       int e = u.i.se & 0x7fff;
+       long double y;
 
-       if (e < MANH_SIZE - 1) {
-               if (e < 0) {
-                       /* raise inexact if x != 0 */
-                       if (huge + x > 0.0)
-                               if (u.bits.exp > 0 ||
-                                   (u.bits.manh | u.bits.manl) != 0)
-                                       u.e = u.bits.sign ? -1.0 : 0.0;
-               } else {
-                       uint64_t m = ((1llu << MANH_SIZE) - 1) >> (e + 1);
-                       if (((u.bits.manh & m) | u.bits.manl) == 0)
-                               return x;  /* x is integral */
-                       if (u.bits.sign) {
-#ifdef LDBL_IMPLICIT_NBIT
-                               if (e == 0)
-                                       u.bits.exp++;
-                               else
-#endif
-                               INC_MANH(u, 1llu << (MANH_SIZE - e - 1));
-                       }
-                       /* raise inexact flag */
-                       if (huge + x > 0.0) {
-                               u.bits.manh &= ~m;
-                               u.bits.manl = 0;
-                       }
-               }
-       } else if (e < LDBL_MANT_DIG - 1) {
-               uint64_t m = (uint64_t)-1 >> (64 - LDBL_MANT_DIG + e + 1);
-               if ((u.bits.manl & m) == 0)
-                       return x;  /* x is integral */
-               if (u.bits.sign) {
-                       if (e == MANH_SIZE - 1)
-                               INC_MANH(u, 1);
-                       else {
-                               uint64_t o = u.bits.manl;
-                               u.bits.manl += 1llu << (LDBL_MANT_DIG - e - 1);
-                               if (u.bits.manl < o)   /* got a carry */
-                                       INC_MANH(u, 1);
-                       }
-               }
-               /* raise inexact flag */
-               if (huge + x > 0.0)
-                       u.bits.manl &= ~m;
+       if (e >= 0x3fff+LDBL_MANT_DIG-1 || x == 0)
+               return x;
+       /* y = int(x) - x, where int(x) is an integer neighbor of x */
+       if (u.i.se >> 15)
+               y = x - TOINT + TOINT - x;
+       else
+               y = x + TOINT - TOINT - x;
+       /* special case because of non-nearest rounding modes */
+       if (e <= 0x3fff-1) {
+               FORCE_EVAL(y);
+               return u.i.se >> 15 ? -1 : 0;
        }
-       return u.e;
+       if (y > 0)
+               return x + y - 1;
+       return x + y;
 }
 #endif
index 775c7b8d0d4d206f8ebfedfe483fd6913b0e1eab..81f4e6223b748b59db415725a9b07a8969dfeac4 100644 (file)
@@ -1,90 +1,20 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_rint.c */
-/*
- * ====================================================
- * 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.
- * ====================================================
- */
-/*
- * rint(x)
- * Return x rounded to integral value according to the prevailing
- * rounding mode.
- * Method:
- *      Using floating addition.
- * Exception:
- *      Inexact flag raised if x not equal to rint(x).
- */
-
-#include "libm.h"
-
-static const double
-TWO52[2] = {
-  4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */
- -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */
-};
+#include <math.h>
+#include <stdint.h>
 
 double rint(double x)
 {
-       int32_t i0,j0,sx;
-       uint32_t i,i1;
-       double w,t;
+       union {double f; uint64_t i;} u = {x};
+       int e = u.i>>52 & 0x7ff;
+       int s = u.i>>63;
+       double_t y;
 
-       EXTRACT_WORDS(i0, i1, x);
-       // FIXME: signed shift
-       sx = (i0>>31) & 1;
-       j0 = ((i0>>20)&0x7ff) - 0x3ff;
-       if (j0 < 20) {
-               if (j0 < 0) {
-                       if (((i0&0x7fffffff)|i1) == 0)
-                               return x;
-                       i1 |= i0 & 0x0fffff;
-                       i0 &= 0xfffe0000;
-                       i0 |= ((i1|-i1)>>12) & 0x80000;
-                       SET_HIGH_WORD(x, i0);
-                       STRICT_ASSIGN(double, w, TWO52[sx] + x);
-                       t = w - TWO52[sx];
-                       GET_HIGH_WORD(i0, t);
-                       SET_HIGH_WORD(t, (i0&0x7fffffff)|(sx<<31));
-                       return t;
-               } else {
-                       i = 0x000fffff>>j0;
-                       if (((i0&i)|i1) == 0)
-                               return x; /* x is integral */
-                       i >>= 1;
-                       if (((i0&i)|i1) != 0) {
-                               /*
-                                * Some bit is set after the 0.5 bit.  To avoid the
-                                * possibility of errors from double rounding in
-                                * w = TWO52[sx]+x, adjust the 0.25 bit to a lower
-                                * guard bit.  We do this for all j0<=51.  The
-                                * adjustment is trickiest for j0==18 and j0==19
-                                * since then it spans the word boundary.
-                                */
-                               if (j0 == 19)
-                                       i1 = 0x40000000;
-                               else if (j0 == 18)
-                                       i1 = 0x80000000;
-                               else
-                                       i0 = (i0 & ~i)|(0x20000>>j0);
-                       }
-               }
-       } else if (j0 > 51) {
-               if (j0 == 0x400)
-                       return x+x;  /* inf or NaN */
-               return x;            /* x is integral */
-       } else {
-               i = (uint32_t)0xffffffff>>(j0-20);
-               if ((i1&i) == 0)
-                       return x;    /* x is integral */
-               i >>= 1;
-               if ((i1&i) != 0)
-                       i1 = (i1 & ~i)|(0x40000000>>(j0-20));
-       }
-       INSERT_WORDS(x, i0, i1);
-       STRICT_ASSIGN(double, w, TWO52[sx] + x);
-       return w - TWO52[sx];
+       if (e >= 0x3ff+52)
+               return x;
+       if (s)
+               y = (double)(x - 0x1p52) + 0x1p52;
+       else
+               y = (double)(x + 0x1p52) - 0x1p52;
+       if (y == 0)
+               return s ? -0.0 : 0;
+       return y;
 }
index e8d4496941d924018e1d4753414c3b2a77bbc7b9..9cfc2a261f3c7f758b776859691c65e1bf6654bd 100644 (file)
@@ -1,48 +1,20 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_rintf.c */
-/*
- * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@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.
- * ====================================================
- */
-
-#include "libm.h"
-
-static const float
-TWO23[2] = {
-  8.3886080000e+06, /* 0x4b000000 */
- -8.3886080000e+06, /* 0xcb000000 */
-};
+#include <math.h>
+#include <stdint.h>
 
 float rintf(float x)
 {
-       int32_t i0,j0,sx;
-       float w,t;
+       union {float f; uint32_t i;} u = {x};
+       int e = u.i>>23 & 0xff;
+       int s = u.i>>31;
+       float_t y;
 
-       GET_FLOAT_WORD(i0, x);
-       sx = (i0>>31) & 1;
-       j0 = ((i0>>23)&0xff) - 0x7f;
-       if (j0 < 23) {
-               if (j0 < 0) {
-                       if ((i0&0x7fffffff) == 0)
-                               return x;
-                       STRICT_ASSIGN(float, w, TWO23[sx] + x);
-                       t = w - TWO23[sx];
-                       GET_FLOAT_WORD(i0, t);
-                       SET_FLOAT_WORD(t, (i0&0x7fffffff)|(sx<<31));
-                       return t;
-               }
-               STRICT_ASSIGN(float, w, TWO23[sx] + x);
-               return w - TWO23[sx];
-       }
-       if (j0 == 0x80)
-               return x+x;  /* inf or NaN */
-       return x;            /* x is integral */
+       if (e >= 0x7f+23)
+               return x;
+       if (s)
+               y = (float)(x - 0x1p23f) + 0x1p23f;
+       else
+               y = (float)(x + 0x1p23f) - 0x1p23f;
+       if (y == 0)
+               return s ? -0.0f : 0.0f;
+       return y;
 }
index b13cfeb30bbf94b8f3742863f6366b6f99b7a680..267250737f0a8e3b3924ee7bb662bbce2bbf6a9e 100644 (file)
@@ -1,30 +1,3 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_rintl.c */
-/*-
- * Copyright (c) 2008 David Schultz <das@FreeBSD.ORG>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
 #include "libm.h"
 
 #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
@@ -33,53 +6,26 @@ long double rintl(long double x)
        return rint(x);
 }
 #elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
-
-#define BIAS    (LDBL_MAX_EXP - 1)
-
-static const float
-shift[2] = {
 #if LDBL_MANT_DIG == 64
-       0x1.0p63, -0x1.0p63
+#define TOINT 0x1p63
 #elif LDBL_MANT_DIG == 113
-       0x1.0p112, -0x1.0p112
+#define TOINT 0x1p112
 #endif
-};
-static const float zero[2] = { 0.0, -0.0 };
-
 long double rintl(long double x)
 {
-       union IEEEl2bits u;
-       uint32_t expsign;
-       int ex, sign;
-
-       u.e = x;
-       expsign = u.xbits.expsign;
-       ex = expsign & 0x7fff;
-
-       if (ex >= BIAS + LDBL_MANT_DIG - 1) {
-               if (ex == BIAS + LDBL_MAX_EXP)
-                       return x + x; /* Inf, NaN, or unsupported format */
-               return x;             /* finite and already an integer */
-       }
-       sign = expsign >> 15;
-
-       /*
-        * The following code assumes that intermediate results are
-        * evaluated in long double precision. If they are evaluated in
-        * greater precision, double rounding may occur, and if they are
-        * evaluated in less precision (as on i386), results will be
-        * wildly incorrect.
-        */
-       x += shift[sign];
-       x -= shift[sign];
-
-       /*
-        * If the result is +-0, then it must have the same sign as x, but
-        * the above calculation doesn't always give this.  Fix up the sign.
-        */
-       if (ex < BIAS && x == 0.0)
-               return zero[sign];
-
-       return x;
+       union ldshape u = {x};
+       int e = u.i.se & 0x7fff;
+       int s = u.i.se >> 15;
+       long double y;
+
+       if (e >= 0x3fff+LDBL_MANT_DIG-1)
+               return x;
+       if (s)
+               y = x - TOINT + TOINT;
+       else
+               y = x + TOINT - TOINT;
+       if (y == 0)
+               return 0*x;
+       return y;
 }
 #endif
index bf0b453fadd67e15548a42f0290a80d2d55cb0d1..4b38d1fdbb2adca6f1f89a8ff86dc29e9206f272 100644 (file)
@@ -1,48 +1,28 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_round.c */
-/*-
- * Copyright (c) 2003, Steven G. Kargl
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice unmodified, this list of conditions, and the following
- *    disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <math.h>
+#include "libm.h"
 
 double round(double x)
 {
-       double t;
+       union {double f; uint64_t i;} u = {x};
+       int e = u.i >> 52 & 0x7ff;
+       double_t y;
 
-       if (!isfinite(x))
+       if (e >= 0x3ff+52)
                return x;
-
-       if (x >= 0.0) {
-               t = floor(x);
-               if (t - x <= -0.5)
-                       t += 1.0;
-               return t;
-       } else {
-               t = floor(-x);
-               if (t + x <= -0.5)
-                       t += 1.0;
-               return -t;
+       if (u.i >> 63)
+               x = -x;
+       if (e < 0x3ff-1) {
+               /* raise inexact if x!=0 */
+               FORCE_EVAL(x + 0x1p52);
+               return 0*u.f;
        }
+       y = (double)(x + 0x1p52) - 0x1p52 - x;
+       if (y > 0.5)
+               y = y + x - 1;
+       else if (y <= -0.5)
+               y = y + x + 1;
+       else
+               y = y + x;
+       if (u.i >> 63)
+               y = -y;
+       return y;
 }
index 662a454b96d3d38ca5787b3f2820b14b44c37e0d..c6b277979038e0abd5ddc566342e674c8d570810 100644 (file)
@@ -1,48 +1,27 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_roundf.c */
-/*-
- * Copyright (c) 2003, Steven G. Kargl
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice unmodified, this list of conditions, and the following
- *    disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <math.h>
+#include "libm.h"
 
 float roundf(float x)
 {
-       float t;
+       union {float f; uint32_t i;} u = {x};
+       int e = u.i >> 23 & 0xff;
+       float_t y;
 
-       if (!isfinite(x))
+       if (e >= 0x7f+23)
                return x;
-
-       if (x >= 0.0) {
-               t = floorf(x);
-               if (t - x <= -0.5)
-                       t += 1.0;
-               return t;
-       } else {
-               t = floorf(-x);
-               if (t + x <= -0.5)
-                       t += 1.0;
-               return -t;
+       if (u.i >> 31)
+               x = -x;
+       if (e < 0x7f-1) {
+               FORCE_EVAL(x + 0x1p23f);
+               return 0*u.f;
        }
+       y = (float)(x + 0x1p23f) - 0x1p23f - x;
+       if (y > 0.5f)
+               y = y + x - 1;
+       else if (y <= -0.5f)
+               y = y + x + 1;
+       else
+               y = y + x;
+       if (u.i >> 31)
+               y = -y;
+       return y;
 }
index 99146f07a5fbd74ad29cc2f1ad310fbc7632bbab..8f3f28b348e131fc2bc7f6c3098e398c75f75a67 100644 (file)
@@ -1,56 +1,39 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_roundl.c */
-/*-
- * Copyright (c) 2003, Steven G. Kargl
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice unmodified, this list of conditions, and the following
- *    disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <math.h>
-#include <float.h>
+#include "libm.h"
 
 #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
 long double roundl(long double x)
 {
        return round(x);
 }
-#else
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+#if LDBL_MANT_DIG == 64
+#define TOINT 0x1p63
+#elif LDBL_MANT_DIG == 113
+#define TOINT 0x1p112
+#endif
 long double roundl(long double x)
 {
-       long double t;
+       union ldshape u = {x};
+       int e = u.i.se & 0x7fff;
+       long double y;
 
-       if (!isfinite(x))
+       if (e >= 0x3fff+LDBL_MANT_DIG-1)
                return x;
-
-       if (x >= 0.0) {
-               t = floorl(x);
-               if (t - x <= -0.5)
-                       t += 1.0;
-               return t;
-       } else {
-               t = floorl(-x);
-               if (t + x <= -0.5)
-                       t += 1.0;
-               return -t;
+       if (u.i.se >> 15)
+               x = -x;
+       if (e < 0x3fff-1) {
+               FORCE_EVAL(x + TOINT);
+               return 0*u.f;
        }
+       y = x + TOINT - TOINT - x;
+       if (y > 0.5)
+               y = y + x - 1;
+       else if (y <= -0.5)
+               y = y + x + 1;
+       else
+               y = y + x;
+       if (u.i.se >> 15)
+               y = -y;
+       return y;
 }
 #endif
index 44b04ecc72d57dd4f44c15b48551a956cc706514..d13711b5015e073fbe69598cdf61f5126cb89df4 100644 (file)
@@ -1,63 +1,19 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_trunc.c */
-/*
- * ====================================================
- * 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.
- * ====================================================
- */
-/*
- * trunc(x)
- * Return x rounded toward 0 to integral value
- * Method:
- *      Bit twiddling.
- * Exception:
- *      Inexact flag raised if x not equal to trunc(x).
- */
-
 #include "libm.h"
 
-static const double huge = 1.0e300;
-
 double trunc(double x)
 {
-       int32_t i0,i1,j0;
-       uint32_t i;
+       union {double f; uint64_t i;} u = {x};
+       int e = (int)(u.i >> 52 & 0x7ff) - 0x3ff + 12;
+       uint64_t m;
 
-       EXTRACT_WORDS(i0, i1, x);
-       j0 = ((i0>>20)&0x7ff) - 0x3ff;
-       if (j0 < 20) {
-               if (j0 < 0) { /* |x|<1, return 0*sign(x) */
-                       /* raise inexact if x != 0 */
-                       if (huge+x > 0.0) {
-                               i0 &= 0x80000000U;
-                               i1 = 0;
-                       }
-               } else {
-                       i = 0x000fffff>>j0;
-                       if (((i0&i)|i1) == 0)
-                               return x; /* x is integral */
-                       /* raise inexact */
-                       if (huge+x > 0.0) {
-                               i0 &= ~i;
-                               i1 = 0;
-                       }
-               }
-       } else if (j0 > 51) {
-               if (j0 == 0x400)
-                       return x + x;  /* inf or NaN */
-               return x;              /* x is integral */
-       } else {
-               i = (uint32_t)0xffffffff>>(j0-20);
-               if ((i1&i) == 0)
-                       return x;      /* x is integral */
-               /* raise inexact */
-               if (huge+x > 0.0)
-                       i1 &= ~i;
-       }
-       INSERT_WORDS(x, i0, i1);
-       return x;
+       if (e >= 52 + 12)
+               return x;
+       if (e < 12)
+               e = 1;
+       m = -1ULL >> e;
+       if ((u.i & m) == 0)
+               return x;
+       FORCE_EVAL(x + 0x1p120f);
+       u.i &= ~m;
+       return u.f;
 }
index 0afcdfbf7c0389f0da1b4826e261bdd99f8f9c4d..1a7d03c3bce103bffad9a74d701ebc9beb9bad17 100644 (file)
@@ -1,52 +1,19 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_truncf.c */
-/*
- * ====================================================
- * 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.
- * ====================================================
- */
-/*
- * truncf(x)
- * Return x rounded toward 0 to integral value
- * Method:
- *      Bit twiddling.
- * Exception:
- *      Inexact flag raised if x not equal to truncf(x).
- */
-
 #include "libm.h"
 
-static const float huge = 1.0e30f;
-
 float truncf(float x)
 {
-       int32_t i0,j0;
-       uint32_t i;
+       union {float f; uint32_t i;} u = {x};
+       int e = (int)(u.i >> 23 & 0xff) - 0x7f + 9;
+       uint32_t m;
 
-       GET_FLOAT_WORD(i0, x);
-       j0 = ((i0>>23)&0xff) - 0x7f;
-       if (j0 < 23) {
-               if (j0 < 0) {  /* |x|<1, return 0*sign(x) */
-                       /* raise inexact if x != 0 */
-                       if (huge+x > 0.0f)
-                               i0 &= 0x80000000;
-               } else {
-                       i = 0x007fffff>>j0;
-                       if ((i0&i) == 0)
-                               return x; /* x is integral */
-                       /* raise inexact */
-                       if (huge+x > 0.0f)
-                               i0 &= ~i;
-               }
-       } else {
-               if (j0 == 0x80)
-                       return x + x;  /* inf or NaN */
-               return x;              /* x is integral */
-       }
-       SET_FLOAT_WORD(x, i0);
-       return x;
+       if (e >= 23 + 9)
+               return x;
+       if (e < 9)
+               e = 1;
+       m = -1U >> e;
+       if ((u.i & m) == 0)
+               return x;
+       FORCE_EVAL(x + 0x1p120f);
+       u.i &= ~m;
+       return u.f;
 }
index d817e9ad0318db81860d72a7dfdd3a1a6bc288bb..3eedb0839713144cf1fbb2f2e05cd7ba6f04aebc 100644 (file)
@@ -1,23 +1,3 @@
-/* origin: FreeBSD /usr/src/lib/msun/src/s_truncl.c */
-/*
- * ====================================================
- * 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.
- * ====================================================
- */
-/*
- * truncl(x)
- * Return x rounded toward 0 to integral value
- * Method:
- *      Bit twiddling.
- * Exception:
- *      Inexact flag raised if x not equal to truncl(x).
- */
-
 #include "libm.h"
 
 #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
@@ -26,43 +6,31 @@ long double truncl(long double x)
        return trunc(x);
 }
 #elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
-#ifdef LDBL_IMPLICIT_NBIT
-#define MANH_SIZE       (LDBL_MANH_SIZE + 1)
-#else
-#define MANH_SIZE       LDBL_MANH_SIZE
+#if LDBL_MANT_DIG == 64
+#define TOINT 0x1p63
+#elif LDBL_MANT_DIG == 113
+#define TOINT 0x1p112
 #endif
-
-static const long double huge = 1.0e300;
-static const float zero[] = { 0.0, -0.0 };
-
 long double truncl(long double x)
 {
-       union IEEEl2bits u = { .e = x };
-       int e = u.bits.exp - LDBL_MAX_EXP + 1;
+       union ldshape u = {x};
+       int e = u.i.se & 0x7fff;
+       int s = u.i.se >> 15;
+       long double y;
 
-       if (e < MANH_SIZE - 1) {
-               if (e < 0) {
-                       /* raise inexact if x != 0 */
-                       if (huge + x > 0.0)
-                               u.e = zero[u.bits.sign];
-               } else {
-                       uint64_t m = ((1llu << MANH_SIZE) - 1) >> (e + 1);
-                       if (((u.bits.manh & m) | u.bits.manl) == 0)
-                               return x;     /* x is integral */
-                       /* raise inexact */
-                       if (huge + x > 0.0) {
-                               u.bits.manh &= ~m;
-                               u.bits.manl = 0;
-                       }
-               }
-       } else if (e < LDBL_MANT_DIG - 1) {
-               uint64_t m = (uint64_t)-1 >> (64 - LDBL_MANT_DIG + e + 1);
-               if ((u.bits.manl & m) == 0)
-                       return x;     /* x is integral */
-               /* raise inexact */
-               if (huge + x > 0.0)
-                       u.bits.manl &= ~m;
+       if (e >= 0x3fff+LDBL_MANT_DIG-1)
+               return x;
+       if (e <= 0x3fff-1) {
+               FORCE_EVAL(x + 0x1p120f);
+               return x*0;
        }
-       return u.e;
+       /* y = int(|x|) - |x|, where int(|x|) is an integer neighbor of |x| */
+       if (s)
+               x = -x;
+       y = x + TOINT - TOINT - x;
+       if (y > 0)
+               y -= 1;
+       x += y;
+       return s ? -x : x;
 }
 #endif