1 /* Compute x * y + z as ternary operation.
2 Copyright (C) 2011-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
19 #define NO_MATH_REDIRECT
23 #include <math-barriers.h>
24 #include <math_private.h>
25 #include <fenv_private.h>
26 #include <math-underflow.h>
27 #include <math_ldbl_opt.h>
28 #include <mul_split.h>
31 /* Calculate X + Y exactly and store the result in *HI + *LO. It is
32 given that |X| >= |Y| and the values are small enough that no
36 add_split (double *hi
, double *lo
, double x
, double y
)
38 /* Apply Dekker's algorithm. */
43 /* Value with extended range, used in intermediate computations. */
46 /* Value in [0.5, 1), as from frexp, or 0. */
48 /* Exponent of power of 2 it is multiplied by, or 0 for zero. */
52 /* Store D as an ext_val value. */
55 store_ext_val (ext_val
*v
, double d
)
57 v
->val
= __frexp (d
, &v
->exp
);
60 /* Store X * Y as ext_val values *V0 and *V1. */
63 mul_ext_val (ext_val
*v0
, ext_val
*v1
, double x
, double y
)
66 x
= __frexp (x
, &xexp
);
67 y
= __frexp (y
, &yexp
);
69 mul_split (&hi
, &lo
, x
, y
);
70 store_ext_val (v0
, hi
);
72 v0
->exp
+= xexp
+ yexp
;
73 store_ext_val (v1
, lo
);
75 v1
->exp
+= xexp
+ yexp
;
78 /* Compare absolute values of ext_val values pointed to by P and Q for
82 compare (const void *p
, const void *q
)
84 const ext_val
*pe
= p
;
85 const ext_val
*qe
= q
;
87 return qe
->val
== 0 ? 0 : -1;
88 else if (qe
->val
== 0)
90 else if (pe
->exp
< qe
->exp
)
92 else if (pe
->exp
> qe
->exp
)
96 double pd
= fabs (pe
->val
);
97 double qd
= fabs (qe
->val
);
107 /* Calculate *X + *Y exactly, storing the high part in *X (rounded to
108 nearest) and the low part in *Y. It is given that |X| >= |Y|. */
111 add_split_ext (ext_val
*x
, ext_val
*y
)
113 int xexp
= x
->exp
, yexp
= y
->exp
;
114 if (y
->val
== 0 || xexp
- yexp
> 53)
117 double lo
= __scalbn (y
->val
, yexp
- xexp
);
118 add_split (&hi
, &lo
, hi
, lo
);
119 store_ext_val (x
, hi
);
122 store_ext_val (y
, lo
);
128 __fmal (long double x
, long double y
, long double z
)
130 double xhi
, xlo
, yhi
, ylo
, zhi
, zlo
;
132 int xexp
, yexp
, zexp
;
135 ldbl_unpack (x
, &xhi
, &xlo
);
136 EXTRACT_WORDS64 (hx
, xhi
);
137 xexp
= (hx
& 0x7ff0000000000000LL
) >> 52;
138 ldbl_unpack (y
, &yhi
, &ylo
);
139 EXTRACT_WORDS64 (hy
, yhi
);
140 yexp
= (hy
& 0x7ff0000000000000LL
) >> 52;
141 ldbl_unpack (z
, &zhi
, &zlo
);
142 EXTRACT_WORDS64 (hz
, zhi
);
143 zexp
= (hz
& 0x7ff0000000000000LL
) >> 52;
145 /* If z is Inf or NaN, but x and y are finite, avoid any exceptions
146 from computing x * y. */
147 if (zexp
== 0x7ff && xexp
!= 0x7ff && yexp
!= 0x7ff)
150 /* If z is zero and x are y are nonzero, compute the result as x * y
151 to avoid the wrong sign of a zero result if x * y underflows to
153 if (z
== 0 && x
!= 0 && y
!= 0)
156 /* If x or y or z is Inf/NaN, or if x * y is zero, compute as x * y
158 if (xexp
== 0x7ff || yexp
== 0x7ff || zexp
== 0x7ff
163 SET_RESTORE_ROUND (FE_TONEAREST
);
166 store_ext_val (&vals
[0], zhi
);
167 store_ext_val (&vals
[1], zlo
);
168 mul_ext_val (&vals
[2], &vals
[3], xhi
, yhi
);
169 mul_ext_val (&vals
[4], &vals
[5], xhi
, ylo
);
170 mul_ext_val (&vals
[6], &vals
[7], xlo
, yhi
);
171 mul_ext_val (&vals
[8], &vals
[9], xlo
, ylo
);
172 qsort (vals
, 10, sizeof (ext_val
), compare
);
173 /* Add up the values so that each element of VALS has absolute
174 value at most equal to the last set bit of the next nonzero
176 for (size_t i
= 0; i
<= 8; i
++)
178 add_split_ext (&vals
[i
+ 1], &vals
[i
]);
179 qsort (vals
+ i
+ 1, 9 - i
, sizeof (ext_val
), compare
);
181 /* Add up the values in the other direction, so that each element
182 of VALS has absolute value less than 5ulp of the next
185 for (size_t i
= 1; i
<= 9; i
++)
187 if (vals
[dstpos
].val
== 0)
189 vals
[dstpos
] = vals
[9 - i
];
195 add_split_ext (&vals
[dstpos
], &vals
[9 - i
]);
196 if (vals
[9 - i
].val
!= 0)
198 if (9 - i
< dstpos
- 1)
200 vals
[dstpos
- 1] = vals
[9 - i
];
208 /* If the result is an exact zero, it results from adding two
209 values with opposite signs; recompute in the original rounding
211 if (vals
[9].val
== 0)
213 /* Adding the top three values will now give a result as accurate
214 as the underlying long double arithmetic. */
215 add_split_ext (&vals
[9], &vals
[8]);
216 if (compare (&vals
[8], &vals
[7]) < 0)
218 ext_val tmp
= vals
[7];
222 add_split_ext (&vals
[8], &vals
[7]);
223 add_split_ext (&vals
[9], &vals
[8]);
224 if (vals
[9].exp
> DBL_MAX_EXP
|| vals
[9].exp
< DBL_MIN_EXP
)
226 /* Overflow or underflow, with the result depending on the
227 original rounding mode, but not on the low part computed
229 scale_val
= vals
[9].val
;
230 scale_exp
= vals
[9].exp
;
233 double hi
= __scalbn (vals
[9].val
, vals
[9].exp
);
234 double lo
= __scalbn (vals
[8].val
, vals
[8].exp
);
235 /* It is possible that the low part became subnormal and was
236 rounded so that the result is no longer canonical. */
237 ldbl_canonicalize (&hi
, &lo
);
238 long double ret
= ldbl_pack (hi
, lo
);
239 math_check_force_underflow (ret
);
244 scale_val
= math_opt_barrier (scale_val
);
245 scale_val
= __scalbn (scale_val
, scale_exp
);
246 if (fabs (scale_val
) == DBL_MAX
)
247 return copysignl (LDBL_MAX
, scale_val
);
248 math_check_force_underflow (scale_val
);
253 zero
= math_opt_barrier (zero
);
257 long_double_symbol (libm
, __fmal
, fmal
);
259 long_double_symbol (libc
, __fmal
, fmal
);