aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Myers <joseph@codesourcery.com>2014-11-04 16:34:49 +0000
committerJoseph Myers <joseph@codesourcery.com>2014-11-04 16:34:49 +0000
commit2004e7fb76bd6806253436d63ab3bda7e24c9cc1 (patch)
treef3b91ec17f5d05879524bc75c2fd67d12c4290e0 /soft-fp
parentRemove unused include (diff)
downloadglibc-2004e7fb76bd6806253436d63ab3bda7e24c9cc1.tar.gz
glibc-2004e7fb76bd6806253436d63ab3bda7e24c9cc1.tar.bz2
glibc-2004e7fb76bd6806253436d63ab3bda7e24c9cc1.zip
soft-fp: Add _FP_TO_INT_ROUND.
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.
Diffstat (limited to 'soft-fp')
-rw-r--r--soft-fp/double.h4
-rw-r--r--soft-fp/extended.h4
-rw-r--r--soft-fp/op-common.h212
-rw-r--r--soft-fp/quad.h4
-rw-r--r--soft-fp/single.h2
5 files changed, 226 insertions, 0 deletions
diff --git a/soft-fp/double.h b/soft-fp/double.h
index 36a11a5452..96d2432aa2 100644
--- a/soft-fp/double.h
+++ b/soft-fp/double.h
@@ -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)
diff --git a/soft-fp/extended.h b/soft-fp/extended.h
index d16fa66174..117abfc539 100644
--- a/soft-fp/extended.h
+++ b/soft-fp/extended.h
@@ -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)
diff --git a/soft-fp/op-common.h b/soft-fp/op-common.h
index 73fbe7af25..67a7818716 100644
--- a/soft-fp/op-common.h
+++ b/soft-fp/op-common.h
@@ -1525,6 +1525,218 @@
} \
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) \
diff --git a/soft-fp/quad.h b/soft-fp/quad.h
index b8cd3108a3..8b7f0a04c5 100644
--- a/soft-fp/quad.h
+++ b/soft-fp/quad.h
@@ -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)
diff --git a/soft-fp/single.h b/soft-fp/single.h
index 086a558b9e..83c0de6c73 100644
--- a/soft-fp/single.h
+++ b/soft-fp/single.h
@@ -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)