xref: /freebsd/contrib/llvm-project/libc/src/__support/FPUtil/DivisionAndRemainderOperations.h (revision bb722a7d0f1642bff6487f943ad0427799a6e5bf)
1*bb722a7dSDimitry Andric //===-- Floating point divsion and remainder operations ---------*- C++ -*-===//
2*bb722a7dSDimitry Andric //
3*bb722a7dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*bb722a7dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*bb722a7dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*bb722a7dSDimitry Andric //
7*bb722a7dSDimitry Andric //===----------------------------------------------------------------------===//
8*bb722a7dSDimitry Andric 
9*bb722a7dSDimitry Andric #ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_DIVISIONANDREMAINDEROPERATIONS_H
10*bb722a7dSDimitry Andric #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_DIVISIONANDREMAINDEROPERATIONS_H
11*bb722a7dSDimitry Andric 
12*bb722a7dSDimitry Andric #include "FPBits.h"
13*bb722a7dSDimitry Andric #include "ManipulationFunctions.h"
14*bb722a7dSDimitry Andric #include "NormalFloat.h"
15*bb722a7dSDimitry Andric 
16*bb722a7dSDimitry Andric #include "src/__support/CPP/type_traits.h"
17*bb722a7dSDimitry Andric #include "src/__support/common.h"
18*bb722a7dSDimitry Andric #include "src/__support/macros/config.h"
19*bb722a7dSDimitry Andric 
20*bb722a7dSDimitry Andric namespace LIBC_NAMESPACE_DECL {
21*bb722a7dSDimitry Andric namespace fputil {
22*bb722a7dSDimitry Andric 
23*bb722a7dSDimitry Andric static constexpr int QUOTIENT_LSB_BITS = 3;
24*bb722a7dSDimitry Andric 
25*bb722a7dSDimitry Andric // The implementation is a bit-by-bit algorithm which uses integer division
26*bb722a7dSDimitry Andric // to evaluate the quotient and remainder.
27*bb722a7dSDimitry Andric template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
remquo(T x,T y,int & q)28*bb722a7dSDimitry Andric LIBC_INLINE T remquo(T x, T y, int &q) {
29*bb722a7dSDimitry Andric   FPBits<T> xbits(x), ybits(y);
30*bb722a7dSDimitry Andric   if (xbits.is_nan())
31*bb722a7dSDimitry Andric     return x;
32*bb722a7dSDimitry Andric   if (ybits.is_nan())
33*bb722a7dSDimitry Andric     return y;
34*bb722a7dSDimitry Andric   if (xbits.is_inf() || ybits.is_zero())
35*bb722a7dSDimitry Andric     return FPBits<T>::quiet_nan().get_val();
36*bb722a7dSDimitry Andric 
37*bb722a7dSDimitry Andric   if (xbits.is_zero()) {
38*bb722a7dSDimitry Andric     q = 0;
39*bb722a7dSDimitry Andric     return LIBC_NAMESPACE::fputil::copysign(T(0.0), x);
40*bb722a7dSDimitry Andric   }
41*bb722a7dSDimitry Andric 
42*bb722a7dSDimitry Andric   if (ybits.is_inf()) {
43*bb722a7dSDimitry Andric     q = 0;
44*bb722a7dSDimitry Andric     return x;
45*bb722a7dSDimitry Andric   }
46*bb722a7dSDimitry Andric 
47*bb722a7dSDimitry Andric   const Sign result_sign =
48*bb722a7dSDimitry Andric       (xbits.sign() == ybits.sign() ? Sign::POS : Sign::NEG);
49*bb722a7dSDimitry Andric 
50*bb722a7dSDimitry Andric   // Once we know the sign of the result, we can just operate on the absolute
51*bb722a7dSDimitry Andric   // values. The correct sign can be applied to the result after the result
52*bb722a7dSDimitry Andric   // is evaluated.
53*bb722a7dSDimitry Andric   xbits.set_sign(Sign::POS);
54*bb722a7dSDimitry Andric   ybits.set_sign(Sign::POS);
55*bb722a7dSDimitry Andric 
56*bb722a7dSDimitry Andric   NormalFloat<T> normalx(xbits), normaly(ybits);
57*bb722a7dSDimitry Andric   int exp = normalx.exponent - normaly.exponent;
58*bb722a7dSDimitry Andric   typename NormalFloat<T>::StorageType mx = normalx.mantissa,
59*bb722a7dSDimitry Andric                                        my = normaly.mantissa;
60*bb722a7dSDimitry Andric 
61*bb722a7dSDimitry Andric   q = 0;
62*bb722a7dSDimitry Andric   while (exp >= 0) {
63*bb722a7dSDimitry Andric     unsigned shift_count = 0;
64*bb722a7dSDimitry Andric     typename NormalFloat<T>::StorageType n = mx;
65*bb722a7dSDimitry Andric     for (shift_count = 0; n < my; n <<= 1, ++shift_count)
66*bb722a7dSDimitry Andric       ;
67*bb722a7dSDimitry Andric 
68*bb722a7dSDimitry Andric     if (static_cast<int>(shift_count) > exp)
69*bb722a7dSDimitry Andric       break;
70*bb722a7dSDimitry Andric 
71*bb722a7dSDimitry Andric     exp -= shift_count;
72*bb722a7dSDimitry Andric     if (0 <= exp && exp < QUOTIENT_LSB_BITS)
73*bb722a7dSDimitry Andric       q |= (1 << exp);
74*bb722a7dSDimitry Andric 
75*bb722a7dSDimitry Andric     mx = n - my;
76*bb722a7dSDimitry Andric     if (mx == 0) {
77*bb722a7dSDimitry Andric       q = result_sign.is_neg() ? -q : q;
78*bb722a7dSDimitry Andric       return LIBC_NAMESPACE::fputil::copysign(T(0.0), x);
79*bb722a7dSDimitry Andric     }
80*bb722a7dSDimitry Andric   }
81*bb722a7dSDimitry Andric 
82*bb722a7dSDimitry Andric   NormalFloat<T> remainder(Sign::POS, exp + normaly.exponent, mx);
83*bb722a7dSDimitry Andric 
84*bb722a7dSDimitry Andric   // Since NormalFloat to native type conversion is a truncation operation
85*bb722a7dSDimitry Andric   // currently, the remainder value in the native type is correct as is.
86*bb722a7dSDimitry Andric   // However, if NormalFloat to native type conversion is updated in future,
87*bb722a7dSDimitry Andric   // then the conversion to native remainder value should be updated
88*bb722a7dSDimitry Andric   // appropriately and some directed tests added.
89*bb722a7dSDimitry Andric   T native_remainder(remainder);
90*bb722a7dSDimitry Andric   T absy = ybits.get_val();
91*bb722a7dSDimitry Andric   int cmp = remainder.mul2(1).cmp(normaly);
92*bb722a7dSDimitry Andric   if (cmp > 0) {
93*bb722a7dSDimitry Andric     q = q + 1;
94*bb722a7dSDimitry Andric     if (x >= T(0.0))
95*bb722a7dSDimitry Andric       native_remainder = native_remainder - absy;
96*bb722a7dSDimitry Andric     else
97*bb722a7dSDimitry Andric       native_remainder = absy - native_remainder;
98*bb722a7dSDimitry Andric   } else if (cmp == 0) {
99*bb722a7dSDimitry Andric     if (q & 1) {
100*bb722a7dSDimitry Andric       q += 1;
101*bb722a7dSDimitry Andric       if (x >= T(0.0))
102*bb722a7dSDimitry Andric         native_remainder = -native_remainder;
103*bb722a7dSDimitry Andric     } else {
104*bb722a7dSDimitry Andric       if (x < T(0.0))
105*bb722a7dSDimitry Andric         native_remainder = -native_remainder;
106*bb722a7dSDimitry Andric     }
107*bb722a7dSDimitry Andric   } else {
108*bb722a7dSDimitry Andric     if (x < T(0.0))
109*bb722a7dSDimitry Andric       native_remainder = -native_remainder;
110*bb722a7dSDimitry Andric   }
111*bb722a7dSDimitry Andric 
112*bb722a7dSDimitry Andric   q = result_sign.is_neg() ? -q : q;
113*bb722a7dSDimitry Andric   if (native_remainder == T(0.0))
114*bb722a7dSDimitry Andric     return LIBC_NAMESPACE::fputil::copysign(T(0.0), x);
115*bb722a7dSDimitry Andric   return native_remainder;
116*bb722a7dSDimitry Andric }
117*bb722a7dSDimitry Andric 
118*bb722a7dSDimitry Andric } // namespace fputil
119*bb722a7dSDimitry Andric } // namespace LIBC_NAMESPACE_DECL
120*bb722a7dSDimitry Andric 
121*bb722a7dSDimitry Andric #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_DIVISIONANDREMAINDEROPERATIONS_H
122