]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
soft-fp: Add _FP_TO_INT_ROUND.
authorJoseph Myers <joseph@codesourcery.com>
Tue, 4 Nov 2014 16:34:49 +0000 (16:34 +0000)
committerJoseph Myers <joseph@codesourcery.com>
Tue, 4 Nov 2014 16:34:49 +0000 (16:34 +0000)
Continuing the series of patches adding soft-fp features from the
kernel version of soft-fp to glibc so that glibc's version is able to
replace the old fork of soft-fp in the kernel, this patch adds the
last major such feature: _FP_TO_INT_ROUND, converting a floating-point
number to an integer with rounding according to the current rounding
direction (as opposed to truncating towards zero, which _FP_TO_INT
does).

The general structure of the implementation follows that of
_FP_TO_INT, but of course is more complicated.  As with glibc's
_FP_TO_INT it works with raw input (the kernel versions of these
macros predate the conversion of _FP_TO_INT and many other macros to
raw or semi-raw input).  I have not tried to work out what bugs there
might be in the kernel version that this might fix; it's a
from-scratch implementation based on _FP_TO_INT.

Tested for powerpc (soft-float) that there is no change to the
installed shared libraries; also tested with the libm tests with lrint
/ lrintf / llrint / llrintf made to use _FP_TO_INT_ROUND, to provide
some test of the functionality.  As we don't have benchmarks for those
functions, I haven't actually included the soft-fp versions of them,
although I expect them to be faster than the existing code (given that
the existing code involves adding and subtracting numbers such as
0x1p52 to achieve the desired rounding, which is not particularly
efficient when the underlying floating point is software floating
point).

2014-11-04  Joseph Myers  <joseph@codesourcery.com>

* soft-fp/op-common.h (_FP_TO_INT_ROUND): New macro.
* soft-fp/double.h [_FP_W_TYPE_SIZE < 64] (FP_TO_INT_ROUND_D): New
macro.
[_FP_W_TYPE_SIZE >= 64] (FP_TO_INT_ROUND_D): Likewise.
* soft-fp/extended.h [_FP_W_TYPE_SIZE < 64] (FP_TO_INT_ROUND_E):
New macro.
[_FP_W_TYPE_SIZE >= 64] (FP_TO_INT_ROUND_E): Likewise.
* soft-fp/quad.h [_FP_W_TYPE_SIZE < 64] (FP_TO_INT_ROUND_Q): New
macro.
[_FP_W_TYPE_SIZE >= 64] (FP_TO_INT_ROUND_Q): Likewise.
* soft-fp/single.h (FP_TO_INT_ROUND_S): New macro.

ChangeLog
soft-fp/double.h
soft-fp/extended.h
soft-fp/op-common.h
soft-fp/quad.h
soft-fp/single.h

index a6e6e65c91f498a3bd63a64c0f3c4d08b0b8f180..380aa866c6b4ec67614c073eb05b1dd85b548907 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2014-11-04  Joseph Myers  <joseph@codesourcery.com>
+
+       * soft-fp/op-common.h (_FP_TO_INT_ROUND): New macro.
+       * soft-fp/double.h [_FP_W_TYPE_SIZE < 64] (FP_TO_INT_ROUND_D): New
+       macro.
+       [_FP_W_TYPE_SIZE >= 64] (FP_TO_INT_ROUND_D): Likewise.
+       * soft-fp/extended.h [_FP_W_TYPE_SIZE < 64] (FP_TO_INT_ROUND_E):
+       New macro.
+       [_FP_W_TYPE_SIZE >= 64] (FP_TO_INT_ROUND_E): Likewise.
+       * soft-fp/quad.h [_FP_W_TYPE_SIZE < 64] (FP_TO_INT_ROUND_Q): New
+       macro.
+       [_FP_W_TYPE_SIZE >= 64] (FP_TO_INT_ROUND_Q): Likewise.
+       * soft-fp/single.h (FP_TO_INT_ROUND_S): New macro.
+
 2014-11-04  Andreas Schwab  <schwab@suse.de>
 
        * posix/regex_internal.h: Don't include <locale/elem-hash.h>.
index 36a11a545247caea3749b2ad080c46ecfffc40f4..96d2432aa2f1490e7588ad359022866b2bba0574 100644 (file)
@@ -182,6 +182,8 @@ union _FP_UNION_D
 # define FP_CMP_UNORD_D(r, X, Y, ex)   _FP_CMP_UNORD (D, 2, (r), X, Y, (ex))
 
 # define FP_TO_INT_D(r, X, rsz, rsg)   _FP_TO_INT (D, 2, (r), X, (rsz), (rsg))
+# define FP_TO_INT_ROUND_D(r, X, rsz, rsg)     \
+  _FP_TO_INT_ROUND (D, 2, (r), X, (rsz), (rsg))
 # define FP_FROM_INT_D(X, r, rs, rt)   _FP_FROM_INT (D, 2, X, (r), (rs), rt)
 
 # define _FP_FRAC_HIGH_D(X)    _FP_FRAC_HIGH_2 (X)
@@ -304,6 +306,8 @@ union _FP_UNION_D
 # define FP_CMP_UNORD_D(r, X, Y, ex)   _FP_CMP_UNORD (D, 1, (r), X, Y, (ex))
 
 # define FP_TO_INT_D(r, X, rsz, rsg)   _FP_TO_INT (D, 1, (r), X, (rsz), (rsg))
+# define FP_TO_INT_ROUND_D(r, X, rsz, rsg)     \
+  _FP_TO_INT_ROUND (D, 1, (r), X, (rsz), (rsg))
 # define FP_FROM_INT_D(X, r, rs, rt)   _FP_FROM_INT (D, 1, X, (r), (rs), rt)
 
 # define _FP_FRAC_HIGH_D(X)    _FP_FRAC_HIGH_1 (X)
index d16fa66174d2e87408a4ee305fb19ed2b1d1c04e..117abfc53927d1d2b5178590b359941cf39d2d18 100644 (file)
@@ -292,6 +292,8 @@ union _FP_UNION_E
 # define FP_CMP_UNORD_E(r, X, Y, ex)   _FP_CMP_UNORD (E, 4, (r), X, Y, (ex))
 
 # define FP_TO_INT_E(r, X, rsz, rsg)   _FP_TO_INT (E, 4, (r), X, (rsz), (rsg))
+# define FP_TO_INT_ROUND_E(r, X, rsz, rsg)     \
+  _FP_TO_INT_ROUND (E, 4, (r), X, (rsz), (rsg))
 # define FP_FROM_INT_E(X, r, rs, rt)   _FP_FROM_INT (E, 4, X, (r), (rs), rt)
 
 # define _FP_FRAC_HIGH_E(X)    (X##_f[2])
@@ -494,6 +496,8 @@ union _FP_UNION_E
 # define FP_CMP_UNORD_E(r, X, Y, ex)   _FP_CMP_UNORD (E, 2, (r), X, Y, (ex))
 
 # define FP_TO_INT_E(r, X, rsz, rsg)   _FP_TO_INT (E, 2, (r), X, (rsz), (rsg))
+# define FP_TO_INT_ROUND_E(r, X, rsz, rsg)     \
+  _FP_TO_INT_ROUND (E, 2, (r), X, (rsz), (rsg))
 # define FP_FROM_INT_E(X, r, rs, rt)   _FP_FROM_INT (E, 2, X, (r), (rs), rt)
 
 # define _FP_FRAC_HIGH_E(X)    (X##_f1)
index 73fbe7af25f8f2673dfbe66cbc47199cefaf8675..67a7818716fcb26b1897ea968664b06cb8c36dcd 100644 (file)
     }                                                                  \
   while (0)
 
+/* Convert from floating point to integer, rounding according to the
+   current rounding direction.  Input is raw.  RSIGNED is as for
+   _FP_TO_INT.  */
+#define _FP_TO_INT_ROUND(fs, wc, r, X, rsize, rsigned)                 \
+  do                                                                   \
+    {                                                                  \
+      if (X##_e < _FP_EXPBIAS_##fs)                                    \
+       {                                                               \
+         int _FP_TO_INT_ROUND_rounds_away = 0;                         \
+         if (X##_e == 0)                                               \
+           {                                                           \
+             if (_FP_FRAC_ZEROP_##wc (X))                              \
+               {                                                       \
+                 (r) = 0;                                              \
+                 goto _FP_TO_INT_ROUND_done;                           \
+               }                                                       \
+             else                                                      \
+               {                                                       \
+                 FP_SET_EXCEPTION (FP_EX_DENORM);                      \
+                 if (FP_DENORM_ZERO)                                   \
+                   {                                                   \
+                     (r) = 0;                                          \
+                     goto _FP_TO_INT_ROUND_done;                       \
+                   }                                                   \
+               }                                                       \
+           }                                                           \
+         /* The result is 0, 1 or -1 depending on the rounding mode;   \
+            -1 may cause overflow in the unsigned case.  */            \
+         switch (FP_ROUNDMODE)                                         \
+           {                                                           \
+           case FP_RND_NEAREST:                                        \
+             _FP_TO_INT_ROUND_rounds_away                              \
+               = (X##_e == _FP_EXPBIAS_##fs - 1                        \
+                  && !_FP_FRAC_ZEROP_##wc (X));                        \
+             break;                                                    \
+           case FP_RND_ZERO:                                           \
+             /* _FP_TO_INT_ROUND_rounds_away is already 0.  */         \
+             break;                                                    \
+           case FP_RND_PINF:                                           \
+             _FP_TO_INT_ROUND_rounds_away = !X##_s;                    \
+             break;                                                    \
+           case FP_RND_MINF:                                           \
+             _FP_TO_INT_ROUND_rounds_away = X##_s;                     \
+             break;                                                    \
+           }                                                           \
+         if ((rsigned) == 0 && _FP_TO_INT_ROUND_rounds_away && X##_s)  \
+           {                                                           \
+             /* Result of -1 for an unsigned conversion.  */           \
+             (r) = 0;                                                  \
+             FP_SET_EXCEPTION (FP_EX_INVALID | FP_EX_INVALID_CVI);     \
+           }                                                           \
+         else if ((rsize) == 1 && (rsigned) > 0                        \
+                  && _FP_TO_INT_ROUND_rounds_away && !X##_s)           \
+           {                                                           \
+             /* Converting to a 1-bit signed bit-field, which cannot   \
+                represent +1.  */                                      \
+             (r) = ((rsigned) == 2 ? -1 : 0);                          \
+             FP_SET_EXCEPTION (FP_EX_INVALID | FP_EX_INVALID_CVI);     \
+           }                                                           \
+         else                                                          \
+           {                                                           \
+             (r) = (_FP_TO_INT_ROUND_rounds_away                       \
+                    ? (X##_s ? -1 : 1)                                 \
+                    : 0);                                              \
+             FP_SET_EXCEPTION (FP_EX_INEXACT);                         \
+           }                                                           \
+       }                                                               \
+      else if ((rsigned) == 2                                          \
+              && (X##_e                                                \
+                  >= ((_FP_EXPMAX_##fs                                 \
+                       < _FP_EXPBIAS_##fs + _FP_FRACBITS_##fs + (rsize) - 1) \
+                      ? _FP_EXPMAX_##fs                                \
+                      : _FP_EXPBIAS_##fs + _FP_FRACBITS_##fs + (rsize) - 1))) \
+       {                                                               \
+         /* Overflow resulting in 0.  */                               \
+         (r) = 0;                                                      \
+         FP_SET_EXCEPTION (FP_EX_INVALID                               \
+                           | FP_EX_INVALID_CVI                         \
+                           | ((FP_EX_INVALID_SNAN                      \
+                               && _FP_ISSIGNAN (fs, wc, X))            \
+                              ? FP_EX_INVALID_SNAN                     \
+                              : 0));                                   \
+       }                                                               \
+      else if ((rsigned) != 2                                          \
+              && (X##_e >= (_FP_EXPMAX_##fs < _FP_EXPBIAS_##fs + (rsize) \
+                            ? _FP_EXPMAX_##fs                          \
+                            : (_FP_EXPBIAS_##fs + (rsize)              \
+                               - ((rsigned) > 0 && !X##_s)))           \
+                  || ((rsigned) == 0 && X##_s)))                       \
+       {                                                               \
+         /* Definite overflow (does not require rounding to tell).  */ \
+         if ((rsigned) != 0)                                           \
+           {                                                           \
+             (r) = 1;                                                  \
+             (r) <<= (rsize) - 1;                                      \
+             (r) -= 1 - X##_s;                                         \
+           }                                                           \
+         else                                                          \
+           {                                                           \
+             (r) = 0;                                                  \
+             if (!X##_s)                                               \
+               (r) = ~(r);                                             \
+           }                                                           \
+                                                                       \
+         FP_SET_EXCEPTION (FP_EX_INVALID                               \
+                           | FP_EX_INVALID_CVI                         \
+                           | ((FP_EX_INVALID_SNAN                      \
+                               && _FP_ISSIGNAN (fs, wc, X))            \
+                              ? FP_EX_INVALID_SNAN                     \
+                              : 0));                                   \
+       }                                                               \
+      else                                                             \
+       {                                                               \
+         /* The value is finite, with magnitude at least 1.  If        \
+            the conversion is unsigned, the value is positive.         \
+            If RSIGNED is not 2, the value does not definitely         \
+            overflow by virtue of its exponent, but may still turn     \
+            out to overflow after rounding; if RSIGNED is 2, the       \
+            exponent may be such that the value definitely overflows,  \
+            but at least one mantissa bit will not be shifted out.  */ \
+         int _FP_TO_INT_ROUND_inexact = 0;                             \
+         _FP_FRAC_HIGH_RAW_##fs (X) |= _FP_IMPLBIT_##fs;               \
+         if (X##_e >= _FP_EXPBIAS_##fs + _FP_FRACBITS_##fs - 1)        \
+           {                                                           \
+             /* The value is an integer, no rounding needed.  */       \
+             _FP_FRAC_ASSEMBLE_##wc ((r), X, (rsize));                 \
+             (r) <<= X##_e - _FP_EXPBIAS_##fs - _FP_FRACBITS_##fs + 1; \
+           }                                                           \
+         else                                                          \
+           {                                                           \
+             /* May need to shift in order to round (unless there      \
+                are exactly _FP_WORKBITS fractional bits already).  */ \
+             int _FP_TO_INT_ROUND_rshift                               \
+               = (_FP_FRACBITS_##fs + _FP_EXPBIAS_##fs                 \
+                  - 1 - _FP_WORKBITS - X##_e);                         \
+             if (_FP_TO_INT_ROUND_rshift > 0)                          \
+               _FP_FRAC_SRS_##wc (X, _FP_TO_INT_ROUND_rshift,          \
+                                  _FP_WFRACBITS_##fs);                 \
+             else if (_FP_TO_INT_ROUND_rshift < 0)                     \
+               _FP_FRAC_SLL_##wc (X, -_FP_TO_INT_ROUND_rshift);        \
+             /* Round like _FP_ROUND, but setting                      \
+                _FP_TO_INT_ROUND_inexact instead of directly setting   \
+                the "inexact" exception, since it may turn out we      \
+                should set "invalid" instead.  */                      \
+             if (_FP_FRAC_LOW_##wc (X) & 7)                            \
+               {                                                       \
+                 _FP_TO_INT_ROUND_inexact = 1;                         \
+                 switch (FP_ROUNDMODE)                                 \
+                   {                                                   \
+                   case FP_RND_NEAREST:                                \
+                     _FP_ROUND_NEAREST (wc, X);                        \
+                     break;                                            \
+                   case FP_RND_ZERO:                                   \
+                     _FP_ROUND_ZERO (wc, X);                           \
+                     break;                                            \
+                   case FP_RND_PINF:                                   \
+                     _FP_ROUND_PINF (wc, X);                           \
+                     break;                                            \
+                   case FP_RND_MINF:                                   \
+                     _FP_ROUND_MINF (wc, X);                           \
+                     break;                                            \
+                   }                                                   \
+               }                                                       \
+             _FP_FRAC_SRL_##wc (X, _FP_WORKBITS);                      \
+             _FP_FRAC_ASSEMBLE_##wc ((r), X, (rsize));                 \
+           }                                                           \
+         if ((rsigned) != 0 && X##_s)                                  \
+           (r) = -(r);                                                 \
+         /* An exponent of RSIZE - 1 always needs testing for          \
+            overflow (either directly overflowing, or overflowing      \
+            when rounding up results in 2^RSIZE).  An exponent of      \
+            RSIZE - 2 can overflow for positive values when rounding   \
+            up to 2^(RSIZE-1), but cannot overflow for negative        \
+            values.  Smaller exponents cannot overflow.  */            \
+         if (X##_e >= (_FP_EXPBIAS_##fs + (rsize) - 1                  \
+                       - ((rsigned) > 0 && !X##_s)))                   \
+           {                                                           \
+             if (X##_e > _FP_EXPBIAS_##fs + (rsize) - 1                \
+                 || (X##_e == _FP_EXPBIAS_##fs + (rsize) - 1           \
+                     && (X##_s                                         \
+                         ? (r) != (((typeof (r)) 1) << ((rsize) - 1))  \
+                         : ((rsigned) > 0 || (r) == 0)))               \
+                 || ((rsigned) > 0                                     \
+                     && !X##_s                                         \
+                     && X##_e == _FP_EXPBIAS_##fs + (rsize) - 2        \
+                     && (r) == (((typeof (r)) 1) << ((rsize) - 1))))   \
+               {                                                       \
+                 if ((rsigned) != 2)                                   \
+                   {                                                   \
+                     if ((rsigned) != 0)                               \
+                       {                                               \
+                         (r) = 1;                                      \
+                         (r) <<= (rsize) - 1;                          \
+                         (r) -= 1 - X##_s;                             \
+                       }                                               \
+                     else                                              \
+                       {                                               \
+                         (r) = 0;                                      \
+                         (r) = ~(r);                                   \
+                       }                                               \
+                   }                                                   \
+                 _FP_TO_INT_ROUND_inexact = 0;                         \
+                 FP_SET_EXCEPTION (FP_EX_INVALID | FP_EX_INVALID_CVI); \
+               }                                                       \
+           }                                                           \
+         if (_FP_TO_INT_ROUND_inexact)                                 \
+           FP_SET_EXCEPTION (FP_EX_INEXACT);                           \
+       }                                                               \
+    _FP_TO_INT_ROUND_done: ;                                           \
+    }                                                                  \
+  while (0)
+
 /* Convert integer to fp.  Output is raw.  RTYPE is unsigned even if
    input is signed.  */
 #define _FP_FROM_INT(fs, wc, X, r, rsize, rtype)                       \
index b8cd3108a34d05f2da783dad553c3eee26a0ac65..8b7f0a04c5af4da9f100ca9fdefecd4809cfc842 100644 (file)
@@ -187,6 +187,8 @@ union _FP_UNION_Q
 # define FP_CMP_UNORD_Q(r, X, Y, ex)   _FP_CMP_UNORD (Q, 4, (r), X, Y, (ex))
 
 # define FP_TO_INT_Q(r, X, rsz, rsg)   _FP_TO_INT (Q, 4, (r), X, (rsz), (rsg))
+# define FP_TO_INT_ROUND_Q(r, X, rsz, rsg)     \
+  _FP_TO_INT_ROUND (Q, 4, (r), X, (rsz), (rsg))
 # define FP_FROM_INT_Q(X, r, rs, rt)   _FP_FROM_INT (Q, 4, X, (r), (rs), rt)
 
 # define _FP_FRAC_HIGH_Q(X)    _FP_FRAC_HIGH_4 (X)
@@ -311,6 +313,8 @@ union _FP_UNION_Q
 # define FP_CMP_UNORD_Q(r, X, Y, ex)   _FP_CMP_UNORD (Q, 2, (r), X, Y, (ex))
 
 # define FP_TO_INT_Q(r, X, rsz, rsg)   _FP_TO_INT (Q, 2, (r), X, (rsz), (rsg))
+# define FP_TO_INT_ROUND_Q(r, X, rsz, rsg)     \
+  _FP_TO_INT_ROUND (Q, 2, (r), X, (rsz), (rsg))
 # define FP_FROM_INT_Q(X, r, rs, rt)   _FP_FROM_INT (Q, 2, X, (r), (rs), rt)
 
 # define _FP_FRAC_HIGH_Q(X)    _FP_FRAC_HIGH_2 (X)
index 086a558b9eb7917a7fc6ca9ab87f018b605faaa3..83c0de6c73a1a979ec9ff8e7358eddb0798a359e 100644 (file)
@@ -180,6 +180,8 @@ union _FP_UNION_S
 #define FP_CMP_UNORD_S(r, X, Y, ex)    _FP_CMP_UNORD (S, 1, (r), X, Y, (ex))
 
 #define FP_TO_INT_S(r, X, rsz, rsg)    _FP_TO_INT (S, 1, (r), X, (rsz), (rsg))
+#define FP_TO_INT_ROUND_S(r, X, rsz, rsg)      \
+  _FP_TO_INT_ROUND (S, 1, (r), X, (rsz), (rsg))
 #define FP_FROM_INT_S(X, r, rs, rt)    _FP_FROM_INT (S, 1, X, (r), (rs), rt)
 
 #define _FP_FRAC_HIGH_S(X)     _FP_FRAC_HIGH_1 (X)