]>
Commit | Line | Data |
---|---|---|
6cac323c AZ |
1 | /* Round to integer generic implementation. |
2 | Copyright (C) 2019 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 Library General Public License as | |
7 | published by the Free Software Foundation; either version 2 of the | |
8 | 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 | Library General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Library General Public | |
16 | License along with the GNU C Library; see the file COPYING.LIB. If | |
5a82c748 | 17 | not, see <https://www.gnu.org/licenses/>. */ |
6cac323c AZ |
18 | |
19 | #ifndef _ROUND_TO_INTEGER_H | |
20 | #define _ROUND_TO_INTEGER_H | |
21 | ||
22 | #include <fenv_private.h> | |
23 | ||
24 | enum round_mode | |
25 | { | |
252296c6 | 26 | CEIL, |
a1cb1888 | 27 | FLOOR, |
ae45cf84 | 28 | ROUND, |
e47308c9 | 29 | TRUNC, |
21bd039b AZ |
30 | NEARBYINT, |
31 | RINT | |
6cac323c AZ |
32 | }; |
33 | ||
21bd039b | 34 | static inline fenv_t |
6cac323c AZ |
35 | set_fenv_mode (enum round_mode mode) |
36 | { | |
21bd039b AZ |
37 | fenv_t fe = 0; |
38 | if (mode != RINT) | |
39 | /* Save current FPU rounding mode and inexact state. */ | |
40 | fe = fegetenv_register (); | |
41 | ||
6cac323c AZ |
42 | switch (mode) |
43 | { | |
e47308c9 AZ |
44 | case CEIL: |
45 | __fesetround_inline_nocheck (FE_UPWARD); | |
46 | break; | |
47 | case FLOOR: | |
48 | __fesetround_inline_nocheck (FE_DOWNWARD); | |
49 | break; | |
ae45cf84 | 50 | case TRUNC: |
e47308c9 AZ |
51 | case ROUND: |
52 | __fesetround_inline_nocheck (FE_TOWARDZERO); | |
53 | break; | |
54 | case NEARBYINT: | |
55 | /* Disable FE_INEXACT exception */ | |
56 | reset_fpscr_bit (FPSCR_XE); | |
57 | break; | |
21bd039b AZ |
58 | case RINT: |
59 | break; | |
60 | } | |
61 | return fe; | |
62 | } | |
63 | ||
64 | static inline void | |
65 | reset_fenv_mode (fenv_t fe, enum round_mode mode) | |
66 | { | |
67 | switch (mode) | |
68 | { | |
69 | default: | |
70 | __builtin_mtfsf (0xff, fe); | |
71 | break; | |
72 | case RINT: | |
73 | break; | |
6cac323c | 74 | } |
6cac323c AZ |
75 | } |
76 | ||
77 | static inline float | |
78 | round_to_integer_float (enum round_mode mode, float x) | |
79 | { | |
80 | /* Ensure sNaN input is converted to qNaN. */ | |
81 | if (__glibc_unlikely (isnan (x))) | |
82 | return x + x; | |
83 | ||
84 | if (fabs (x) > 0x1p+23) | |
85 | return x; | |
86 | ||
87 | float r = x; | |
88 | ||
21bd039b | 89 | fenv_t fe = set_fenv_mode (mode); |
6cac323c AZ |
90 | if (x > 0.0) |
91 | { | |
a1cb1888 AZ |
92 | /* IEEE 1003.1 round function. IEEE specifies "round to the nearest |
93 | integer value, rounding halfway cases away from zero, regardless of | |
94 | the current rounding mode." However PowerPC Architecture defines | |
95 | "Round to Nearest" as "Choose the best approximation. In case of a | |
96 | tie, choose the one that is even (least significant bit o).". | |
97 | So we can't use the PowerPC "Round to Nearest" mode. Instead we set | |
98 | "Round toward Zero" mode and round by adding +-0.5 before rounding | |
99 | to the integer value. */ | |
100 | if (mode == ROUND) | |
101 | r += 0.5f; | |
6cac323c AZ |
102 | r += 0x1p+23; |
103 | r -= 0x1p+23; | |
104 | r = fabs (r); | |
105 | } | |
106 | else if (x < 0.0) | |
107 | { | |
a1cb1888 AZ |
108 | if (mode == ROUND) |
109 | r -= 0.5f; | |
6cac323c AZ |
110 | r -= 0x1p+23; |
111 | r += 0x1p+23; | |
112 | r = -fabs (r); | |
113 | } | |
21bd039b | 114 | reset_fenv_mode (fe, mode); |
6cac323c AZ |
115 | |
116 | return r; | |
117 | } | |
118 | ||
119 | static inline double | |
120 | round_to_integer_double (enum round_mode mode, double x) | |
121 | { | |
122 | /* Ensure sNaN input is converted to qNaN. */ | |
123 | if (__glibc_unlikely (isnan (x))) | |
124 | return x + x; | |
125 | ||
126 | if (fabs (x) > 0x1p+52) | |
127 | return x; | |
128 | ||
129 | double r = x; | |
130 | ||
131 | /* Save current FPU rounding mode and inexact state. */ | |
21bd039b | 132 | fenv_t fe = set_fenv_mode (mode); |
6cac323c AZ |
133 | if (x > 0.0) |
134 | { | |
a1cb1888 AZ |
135 | if (mode == ROUND) |
136 | r += 0.5; | |
6cac323c AZ |
137 | r += 0x1p+52; |
138 | r -= 0x1p+52; | |
139 | r = fabs (r); | |
140 | } | |
141 | else if (x < 0.0) | |
142 | { | |
a1cb1888 AZ |
143 | if (mode == ROUND) |
144 | r -= 0.5; | |
6cac323c AZ |
145 | r -= 0x1p+52; |
146 | r += 0x1p+52; | |
147 | r = -fabs (r); | |
148 | } | |
21bd039b | 149 | reset_fenv_mode (fe, mode); |
6cac323c AZ |
150 | |
151 | return r; | |
152 | } | |
153 | ||
154 | #endif |