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