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 }