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