1 module decimal.floats; 2 3 4 5 6 /* ****************************************************************************************************************** */ 7 /* FLOAT UTILITY FUNCTIONS */ 8 /* ****************************************************************************************************************** */ 9 10 private import decimal.integrals: uint128, clz, divrem; 11 12 package: 13 14 enum MAX_FLOAT_COEFFICIENT_34 = uint128("3402823466385288598117041834845169"); 15 enum MAX_FLOAT_EXPONENT_34 = 5; 16 enum MIN_FLOAT_COEFFICIENT_34 = uint128("1401298464324817070923729583289916"); 17 enum MIN_FLOAT_EXPONENT_34 = -78; 18 19 enum MAX_DOUBLE_COEFFICIENT_34 = uint128("1797693134862315708145274237317043"); 20 enum MAX_DOUBLE_EXPONENT_34 = 275; 21 enum MIN_DOUBLE_COEFFICIENT_34 = uint128("4940656458412465441765687928682213"); 22 enum MIN_DOUBLE_EXPONENT_34 = -357; 23 24 enum MAX_REAL_COEFFICIENT_34 = uint128("1189731495357231765021263853030970"); 25 enum MAX_REAL_EXPONENT_34 = 4899; 26 enum MIN_REAL_COEFFICIENT_34 = uint128("3645199531882474602528405933619419"); 27 enum MIN_REAL_EXPONENT_34 = -4984; 28 29 30 31 32 33 34 union FU 35 { 36 float f; 37 uint u; 38 } 39 40 @safe pure nothrow @nogc 41 float fpack(const bool sign, int exp, uint mantissa) 42 { 43 if (mantissa == 0) 44 return sign ? -0.0f : +0.0f; 45 auto shift = clz(mantissa) - 8; 46 if (shift < 0) 47 { 48 if (exp > int.max + shift) 49 return sign ? -float.infinity : +float.infinity; 50 else 51 mantissa >>= -shift; 52 } 53 else 54 { 55 if (exp < int.min + shift) 56 return sign ? -0.0f : +0.0f; 57 mantissa <<= shift; 58 } 59 exp -= shift; 60 61 if (exp > int.max - 150) 62 return sign ? -float.infinity : +float.infinity; 63 exp += 150; 64 65 if (exp >= 0xFF) 66 return sign ? -float.infinity : +float.infinity; 67 68 if (exp <= 0) 69 { 70 --exp; 71 if (exp < -23) 72 return sign ? -0.0f : +0.0f; 73 mantissa >>= -exp; 74 exp = 0; 75 } 76 77 FU fu; 78 fu.u = (mantissa & 0x7FFFFF) | (exp << 23); 79 if (sign) 80 fu.u |= 0x80000000U; 81 return fu.f; 82 } 83 84 @safe pure nothrow @nogc 85 bool funpack(const float f, out int exp, out uint mantissa, out bool inf, out bool nan) 86 { 87 FU fu; 88 fu.f = f; 89 90 exp = (fu.u >> 23) & 0xFF; 91 mantissa = fu.u & 0x7FFFFFU; 92 if (exp == 0) 93 { 94 inf = false; nan = false; 95 if (mantissa) 96 exp -= 149; 97 } 98 else if (exp == 0xFF) 99 { 100 inf = mantissa == 0; 101 nan = !inf; 102 } 103 else 104 { 105 inf = false; nan = false; 106 mantissa |= 0x00800000; 107 exp -= 150; 108 } 109 110 return (fu.u & 0x80000000U) != 0; 111 } 112 113 union DU 114 { 115 double d; 116 ulong u; 117 } 118 119 @safe pure nothrow @nogc 120 double dpack(const bool sign, int exp, ulong mantissa) 121 { 122 if (mantissa == 0) 123 return sign ? -0.0 : +0.0; 124 125 auto shift = clz(mantissa) - 11; 126 if (shift < 0) 127 { 128 if (exp > int.max + shift) 129 return sign ? -double.infinity : +double.infinity; 130 else 131 mantissa >>= -shift; 132 } 133 else 134 { 135 if (exp < int.min + shift) 136 return sign ? -0.0 : +0.0; 137 mantissa <<= shift; 138 } 139 exp -= shift; 140 141 if (exp > int.max - 1075) 142 return sign ? -double.infinity : +double.infinity; 143 exp += 1075; 144 145 if (exp >= 0x7FF) 146 return sign ? -double.infinity : +double.infinity; 147 148 if (exp <= 0) 149 { 150 --exp; 151 if (exp < -52) 152 return sign ? -0.0 : +0.0; 153 mantissa >>= -exp; 154 exp = 0; 155 } 156 157 DU du; 158 du.u = (mantissa & 0x000FFFFFFFFFFFFFUL) | (cast(ulong)exp << 52); 159 if (sign) 160 du.u |= 0x8000000000000000UL; 161 return du.d; 162 } 163 164 @safe pure nothrow @nogc 165 bool dunpack(const double d, out int exp, out ulong mantissa, out bool inf, out bool nan) 166 { 167 DU du; 168 du.d = d; 169 170 exp = (du.u >> 52) & 0x7FF; 171 mantissa = du.u & 0xFFFFFFFFFFFFF; 172 173 if (exp == 0) 174 { 175 inf = false; nan = false; 176 if (mantissa) 177 exp -= 1074; 178 } 179 else if (exp == 0x7FF) 180 { 181 inf = mantissa == 0; 182 nan = !inf; 183 } 184 else 185 { 186 inf = false; nan = false; 187 mantissa |= 0x10000000000000; 188 exp -= 1075; 189 } 190 191 return (du.u & 0x8000000000000000UL) != 0; 192 } 193 194 union RU 195 { 196 real r; 197 struct 198 { //align(1): 199 version(LittleEndian) 200 { 201 ulong m; 202 ushort e; 203 } 204 else 205 { 206 ushort e; 207 ulong m; 208 } 209 } 210 } 211 212 @safe pure nothrow @nogc 213 real rpack(const bool sign, int exp, ulong mantissa) 214 { 215 if (mantissa == 0) 216 return sign ? -0.0L : +0.0L; 217 218 auto shift = clz(mantissa); 219 if (exp < int.min + shift) 220 return sign ? -0.0L : +0.0L; 221 mantissa <<= shift; 222 exp -= shift; 223 224 if (exp > int.max - 16447) //16383 + 64 225 return sign ? -real.infinity : +real.infinity; 226 exp += 16447; 227 228 if (exp >= 0x7FFF) 229 return sign ? -real.infinity : +real.infinity; 230 231 if (exp <= 0) 232 { 233 --exp; 234 if (exp < -64) 235 return sign ? -0.0L : +0.0L; 236 mantissa >>= -exp; 237 exp = 0; 238 } 239 240 RU ru; 241 ru.m = mantissa; 242 ru.e = cast(ushort)exp; 243 if (sign) 244 ru.e |= 0x8000U; 245 return ru.r; 246 } 247 248 @safe pure nothrow @nogc 249 bool runpack(const real r, out int exp, out ulong mantissa, out bool inf, out bool nan) 250 { 251 RU ru; 252 ru.r = r; 253 254 exp = ru.e & 0x7FFF; 255 mantissa = ru.m; 256 257 if (exp == 0) 258 { 259 inf = false; nan = false; 260 if (mantissa) 261 exp -= 16445; 262 } 263 else if (exp == 0x7FFF) 264 { 265 inf = (mantissa & 0x7FFF_FFFF_FFFF_FFFF) == 0; 266 nan = !inf; 267 } 268 else 269 { 270 inf = false; nan = false; 271 exp -= 16446; 272 } 273 274 return (ru.e & 0x8000) != 0; 275 } 276 277 278 279 280 void floatExtract(float f, out uint coefficient, out int exponent) 281 { 282 // x * 2^n = y * 10^m -> 2^n = n * log2(10) 283 } 284 285 @nogc @safe pure nothrow 286 bool exp2to10(ref uint coefficient, ref int exponent) 287 { 288 enum maxMultiplicable = uint.max / 5U; 289 290 bool inexact; 291 292 auto e5 = -exponent; 293 294 295 296 while (e5 > 0) 297 { 298 if (!(coefficient & 1U)) 299 { 300 ++exponent; 301 --e5; 302 coefficient >>>= 1; 303 } 304 else 305 { 306 --e5; 307 if (coefficient <= maxMultiplicable) 308 coefficient *= 5U; 309 else 310 { 311 inexact = true; 312 ++exponent; 313 coefficient >>>= 1; 314 } 315 } 316 } 317 318 while (e5 < 0) 319 { 320 if ((coefficient & 0x80000000U) != 0x80000000U) 321 { 322 --exponent; 323 ++e5; 324 coefficient <<= 1; 325 } 326 else 327 { 328 ++e5; 329 if (coefficient % 5U != 0) 330 inexact = true; 331 coefficient /= 5U; 332 } 333 } 334 335 return inexact; 336 } 337 338 public 339 @nogc @safe pure nothrow 340 bool exp2to10(ref ulong coefficient, ref int exponent) 341 { 342 enum maxMultiplicable = ulong.max / 5UL; 343 344 bool inexact; 345 346 auto e5 = -exponent; 347 348 while (e5 > 0) 349 { 350 if (!(coefficient & 1UL)) 351 { 352 ++exponent; 353 --e5; 354 coefficient >>>= 1; 355 } 356 else 357 { 358 --e5; 359 if (coefficient <= maxMultiplicable) 360 coefficient *= 5UL; 361 else 362 { 363 inexact = true; 364 ++exponent; 365 coefficient >>>= 1; 366 } 367 } 368 } 369 370 while (e5 < 0) 371 { 372 if ((coefficient & 0x8000000000000000UL) != 0x8000000000000000UL) 373 { 374 --exponent; 375 ++e5; 376 coefficient <<= 1; 377 } 378 else 379 { 380 ++e5; 381 if (coefficient % 5UL != 0) 382 inexact = true; 383 coefficient /= 5UL; 384 } 385 } 386 387 return inexact; 388 } 389 390 @nogc @safe pure nothrow 391 bool exp2to10(ref uint128 coefficient, ref int exponent) 392 { 393 enum maxMultiplicable = uint128.max / 5UL; 394 395 bool inexact; 396 397 auto e5 = -exponent; 398 399 while (e5 > 0) 400 { 401 if (!(coefficient & 1UL)) 402 { 403 ++exponent; 404 --e5; 405 coefficient >>>= 1; 406 } 407 else 408 { 409 --e5; 410 if (coefficient <= maxMultiplicable) 411 coefficient *= 5U; 412 else 413 { 414 inexact = true; 415 ++exponent; 416 coefficient >>>= 1; 417 } 418 } 419 } 420 421 while (e5 < 0) 422 { 423 if ((coefficient.hi & 0x8000000000000000UL) != 0x8000000000000000UL) 424 { 425 --exponent; 426 ++e5; 427 coefficient <<= 1; 428 } 429 else 430 { 431 ++e5; 432 if (divrem(coefficient, 5U)) 433 inexact = true; 434 } 435 } 436 437 return inexact; 438 } 439 440