1 /* Round to integer type.  Common helper functions.
2    Copyright (C) 2016-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <errno.h>
20 #include <fenv.h>
21 #include <float.h>
22 #include <math.h>
23 #include <math-barriers.h>
24 #include <stdbool.h>
25 #include <stdint.h>
26 
27 /* The including file should have defined UNSIGNED to 0 (signed return
28    type) or 1 (unsigned return type), INEXACT to 0 (no inexact
29    exceptions) or 1 (raise inexact exceptions) and RET_TYPE to the
30    return type (intmax_t or uintmax_t).  */
31 
32 /* Return the maximum unbiased exponent for an argument (negative if
33    NEGATIVE is set) that might be in range for a call to a fromfp
34    function with width WIDTH (greater than 0, and not exceeding that
35    of intmax_t).  The truncated argument may still be out of range in
36    the case of negative arguments, and if not out of range it may
37    become out of range as a result of rounding.  */
38 
39 static int
fromfp_max_exponent(bool negative,int width)40 fromfp_max_exponent (bool negative, int width)
41 {
42   if (UNSIGNED)
43     return negative ? -1 : width - 1;
44   else
45     return negative ? width - 1 : width - 2;
46 }
47 
48 /* Return the result of rounding an integer value X (passed as the
49    absolute value; NEGATIVE is true if the value is negative), where
50    HALF_BIT is true if the bit with value 0.5 is set and MORE_BITS is
51    true if any lower bits are set, in the rounding direction
52    ROUND.  */
53 
54 static uintmax_t
fromfp_round(bool negative,uintmax_t x,bool half_bit,bool more_bits,int round)55 fromfp_round (bool negative, uintmax_t x, bool half_bit, bool more_bits,
56 	      int round)
57 {
58   switch (round)
59     {
60     case FP_INT_UPWARD:
61       return x + (!negative && (half_bit || more_bits));
62 
63     case FP_INT_DOWNWARD:
64       return x + (negative && (half_bit || more_bits));
65 
66     case FP_INT_TOWARDZERO:
67     default:
68       /* Unknown rounding directions are defined to mean unspecified
69 	 rounding; treat this as truncation.  */
70       return x;
71 
72     case FP_INT_TONEARESTFROMZERO:
73       return x + half_bit;
74 
75     case FP_INT_TONEAREST:
76       return x + (half_bit && ((x & 1) || more_bits));
77     }
78 }
79 
80 /* Integer rounding, of a value whose exponent EXPONENT did not exceed
81    the maximum exponent MAX_EXPONENT and so did not necessarily
82    overflow, has produced X (possibly wrapping to 0); the sign is
83    negative if NEGATIVE is true.  Return whether this overflowed the
84    allowed width.  */
85 
86 static bool
fromfp_overflowed(bool negative,uintmax_t x,int exponent,int max_exponent)87 fromfp_overflowed (bool negative, uintmax_t x, int exponent, int max_exponent)
88 {
89   if (UNSIGNED)
90     {
91       if (negative)
92 	return x != 0;
93       else if (max_exponent == INTMAX_WIDTH - 1)
94 	return exponent == INTMAX_WIDTH - 1 && x == 0;
95       else
96 	return x == (1ULL << (max_exponent + 1));
97     }
98   else
99     {
100       if (negative)
101 	return exponent == max_exponent && x != (1ULL << max_exponent);
102       else
103 	return x == (1ULL << (max_exponent + 1));
104     }
105 }
106 
107 /* Handle a domain error for a call to a fromfp function with an
108    argument which is negative if NEGATIVE is set, and specified width
109    (not exceeding that of intmax_t) WIDTH.  The return value is
110    unspecified (with it being unclear if the result needs to fit
111    within WIDTH bits in this case); we choose to saturate to the given
112    number of bits (treating NaNs like any other value).  */
113 
114 static RET_TYPE
fromfp_domain_error(bool negative,unsigned int width)115 fromfp_domain_error (bool negative, unsigned int width)
116 {
117   feraiseexcept (FE_INVALID);
118   __set_errno (EDOM);
119   /* The return value is unspecified; we choose to saturate to the
120      given number of bits (treating NaNs like any other value).  */
121   if (UNSIGNED)
122     {
123       if (negative)
124 	return 0;
125       else if (width == INTMAX_WIDTH)
126 	return -1;
127       else
128 	return (1ULL << width) - 1;
129     }
130   else
131     {
132       if (width == 0)
133 	return 0;
134       else if (negative)
135 	return -(1ULL << (width - 1));
136       else
137 	return (1ULL << (width - 1)) - 1;
138     }
139 }
140 
141 /* Given X, the absolute value of a floating-point number (negative if
142    NEGATIVE is set) truncated towards zero, where HALF_BIT is true if
143    the bit with value 0.5 is set and MORE_BITS is true if any lower
144    bits are set, round it in the rounding direction ROUND, handle
145    errors and exceptions and return the appropriate return value for a
146    fromfp function.  X originally had floating-point exponent
147    EXPONENT, which does not exceed MAX_EXPONENT, the return value from
148    fromfp_max_exponent with width WIDTH.  */
149 
150 static RET_TYPE
fromfp_round_and_return(bool negative,uintmax_t x,bool half_bit,bool more_bits,int round,int exponent,int max_exponent,unsigned int width)151 fromfp_round_and_return (bool negative, uintmax_t x, bool half_bit,
152 			 bool more_bits, int round, int exponent,
153 			 int max_exponent, unsigned int width)
154 {
155   uintmax_t uret = fromfp_round (negative, x, half_bit, more_bits, round);
156   if (fromfp_overflowed (negative, uret, exponent, max_exponent))
157     return fromfp_domain_error (negative, width);
158 
159   if (INEXACT && (half_bit || more_bits))
160     {
161       /* There is no need for this to use the specific floating-point
162 	 type for which this header is included, and there is no need
163 	 for this header to know that type at all, so just use float
164 	 here.  */
165       float force_inexact = 1.0f + FLT_MIN;
166       math_force_eval (force_inexact);
167     }
168   if (UNSIGNED)
169     /* A negative argument not rounding to zero will already have
170        produced a domain error.  */
171     return uret;
172   else
173     return negative ? -uret : uret;
174 }
175