1 /**
2     Nulib Floating Point Math
3 
4     Copyright:
5         Copyright © 2023-2025, Kitsunebi Games
6         Copyright © 2023-2025, Inochi2D Project
7     
8     License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
9     Authors:
10         Luna Nielsen
11 */
12 module nulib.math.floating;
13 import nulib.math.intrinsics;
14 
15 @safe @nogc nothrow pure:
16 
17 /**
18     Determines whether the given value is NaN (Not a Number)
19 
20     Params:
21         x   = The value to check
22     
23     Returns:
24         $(D true) if $(D x) is NaN,
25         $(D false) otherwise.
26 */
27 bool isNaN(T)(T x) @safe @nogc nothrow pure if (__traits(isFloating, T)) {
28     return x != x;
29 }
30 
31 @("isNaN")
32 unittest {
33     assert(isNaN(float.nan));
34     assert(isNaN(double.nan));
35     assert(isNaN(real.nan));
36     assert(!isNaN(0.0));
37     assert(!isNaN(double.infinity));
38 }
39 
40 /**
41     Determines whether the given value is a finite number.
42 
43     Params:
44         x   = The value to check
45     
46     Returns:
47         $(D true) if $(D x) is a finite, valid number,
48         $(D false) otherwise.
49 */
50 bool isFinite(T)(T x) @safe @nogc nothrow pure if (__traits(isFloating, T)) {
51     return x == x && x != T.infinity && x != -T.infinity;
52 }
53 
54 @("isFinite")
55 unittest {
56     assert(isFinite(1.0));
57     assert(!isFinite(double.nan));
58     assert(!isFinite(real.infinity));
59 }
60 
61 /**
62     Determines whether the given value is an infinite number.
63 
64     Params:
65         x   = The value to check
66     
67     Returns:
68         $(D true) if $(D x) is an infinite floating point number,
69         $(D false) otherwise.
70 */
71 bool isInfinity(T)(T x) @trusted @nogc nothrow pure if (__traits(isFloating, T)) {
72     static if (is(T == float)) {
73         return ((*cast(uint *)&x) & 0x7FFF_FFFF) == 0x7F80_0000;
74     } else static if (is(T == double)) {
75         return ((*cast(ulong *)&x) & 0x7FFF_FFFF_FFFF_FFFF)
76             == 0x7FF0_0000_0000_0000;
77     } else return (x < -T.max) || (T.max < x);
78 }
79 
80 @("isInfinity")
81 unittest {
82     assert(isInfinity(float.infinity));
83     assert(isInfinity(-float.infinity));
84     assert(!isInfinity(double.nan));
85     assert(!isInfinity(0.0));
86 }
87 
88 /**
89     Gets the fractional part of the value.
90 
91     Params:
92         value = The value to get the fractional portion of
93 
94     Returns:
95         The factional part of the given value.
96 */
97 T fract(T)(T value) if(__traits(isFloating, T)) {
98     return cast(T)(cast(real)value - trunc(cast(real)value));
99 }
100 
101 @("fract")
102 unittest {
103     assert(fract(1.5) == 0.5);
104 }
105 
106 /**
107     Computes arc-tangent of the given value.
108 
109     Params:
110         x = The value
111     
112     Returns:
113         The arc-tangent of $(D x).
114 */
115 version(DigitalMars)
116 T atan(T)(T x) @safe @nogc nothrow pure {
117     static if (is(T == float) && T.mant_dig == 24) {
118 
119         static immutable T[4] P = [
120         -3.33329491539E-1,
121             1.99777106478E-1,
122         -1.38776856032E-1,
123             8.05374449538E-2,
124         ];
125     } else static if (is(T == double)) {
126 
127         static immutable T[5] P = [
128         -6.485021904942025371773E1L,
129         -1.228866684490136173410E2L,
130         -7.500855792314704667340E1L,
131         -1.615753718733365076637E1L,
132         -8.750608600031904122785E-1L,
133         ];
134         static immutable T[6] Q = [
135             1.945506571482613964425E2L,
136             4.853903996359136964868E2L,
137             4.328810604912902668951E2L,
138             1.650270098316988542046E2L,
139             2.485846490142306297962E1L,
140             1.000000000000000000000E0L,
141         ];
142 
143         enum T MOREBITS = 6.123233995736765886130E-17L;
144         
145     } else static assert(0, T.stringof~" is not supported currently!");
146 
147     // tan(PI/8)
148     enum T TAN_PI_8 = 0.414213562373095048801688724209698078569672L;
149 
150     // tan(3 * PI/8)
151     enum T TAN3_PI_8 = 2.414213562373095048801688724209698078569672L;
152 
153     // Special cases.
154     if (x == cast(T) 0.0)
155         return x;
156     if (isInfinity(x))
157         return copysign(cast(T) PI_2, x);
158 
159     // Make argument positive but save the sign.
160     bool sign = false;
161     if (signbit(x)) {
162         sign = true;
163         x = -x;
164     }
165 
166     static if (is(T == float) && T.mant_dig == 24) {
167 
168         // Range reduction.
169         T y;
170         if (x > TAN3_PI_8) {
171 
172             y = PI_2;
173             x = -((cast(T) 1.0) / x);
174         } else if (x > TAN_PI_8) {
175             
176             y = PI_4;
177             x = (x - cast(T) 1.0)/(x + cast(T) 1.0);
178         } else y = 0.0;
179 
180         // Rational form in x^^2.
181         const T z = x * x;
182         y += poly(z, P) * z * x + x;
183         
184     } else {
185         short flag = 0;
186         T y;
187         if (x > TAN3_PI_8) {
188             y = PI_2;
189             flag = 1;
190             x = -(1.0 / x);
191         } else if (x <= 0.66) {
192             y = 0.0;
193         } else {
194             y = PI_4;
195             flag = 2;
196             x = (x - 1.0)/(x + 1.0);
197         }
198 
199         T z = x * x;
200         z = z * poly(z, P) / poly(z, Q);
201         z = x * z + x;
202 
203         if (flag == 2)
204             z += 0.5 * MOREBITS;
205         else if (flag == 1)
206             z += MOREBITS;
207         
208         y = y + z;
209     }
210 
211     return sign ? -y : y;
212 }
213 
214 /**
215     Computes arc-tangent of the given value, using signs to determine quadrant.
216 
217     Params:
218         y = value
219         x = value
220     
221     Returns:
222         The arc-tangent of $(D y / x).
223 */
224 version(DigitalMars)
225 T atan2(T)(T y, T x) @safe pure nothrow @nogc {
226 
227     // Special cases.
228     if (isNaN(x) || isNaN(y))
229         return T.nan;
230     
231     if (y == cast(T) 0.0) {
232         if (x >= 0 && !signbit(x))
233             return copysign(0, y);
234         else
235             return copysign(cast(T) PI, y);
236     } if (x == cast(T) 0.0)
237         return copysign(cast(T) PI_2, y);
238     
239     if (isInfinity(x)) {
240         if (signbit(x)) {
241             if (isInfinity(y))
242                 return copysign(3 * cast(T) PI_4, y);
243             else
244                 return copysign(cast(T) PI, y);
245         } else {
246             if (isInfinity(y))
247                 return copysign(cast(T) PI_4, y);
248             else
249                 return copysign(cast(T) 0.0, y);
250         }
251     }
252 
253     if (isInfinity(y))
254         return copysign(cast(T) PI_2, y);
255 
256     // Call atan and determine the quadrant.
257     T z = atan(y / x);
258 
259     if (signbit(x)) {
260         if (signbit(y))
261             z = z - cast(T) PI;
262         else
263             z = z + cast(T) PI;
264     }
265 
266     if (z == cast(T) 0.0)
267         return copysign(z, y);
268 
269     return z;
270 }
271 
272 private:
273 T poly(T)(T x, T[] y) @safe @nogc nothrow pure {
274     ptrdiff_t i = y.length - 1;
275     T r = y[i];
276     while (--i >= 0) {
277         r *= x;
278         r += y[i];
279     }
280     return r;
281 }