xref: /dragonfly/contrib/gmp/mpn/generic/get_d.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1 /* mpn_get_d -- limbs to double conversion.
2 
3    THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY.  THEY'RE ALMOST
4    CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN
5    FUTURE GNU MP RELEASES.
6 
7 Copyright 2003, 2004, 2007, 2009 Free Software Foundation, Inc.
8 
9 This file is part of the GNU MP Library.
10 
11 The GNU MP Library is free software; you can redistribute it and/or modify
12 it under the terms of the GNU Lesser General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or (at your
14 option) any later version.
15 
16 The GNU MP Library is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
19 License for more details.
20 
21 You should have received a copy of the GNU Lesser General Public License
22 along with the GNU MP Library.  If not, see http://www.gnu.org/licenses/.  */
23 
24 #include "gmp.h"
25 #include "gmp-impl.h"
26 #include "longlong.h"
27 
28 #ifndef _GMP_IEEE_FLOATS
29 #define _GMP_IEEE_FLOATS 0
30 #endif
31 
32 #if ! _GMP_IEEE_FLOATS
33 /* dummy definition, just to let dead code compile */
34 union ieee_double_extract {
35   struct {
36     int manh, manl, sig, exp;
37   } s;
38   double d;
39 };
40 #endif
41 
42 /* To force use of the generic C code for testing, put
43    "#define _GMP_IEEE_FLOATS 0" at this point.  */
44 
45 
46 
47 /* In alpha gcc prior to 3.4, signed DI comparisons involving constants are
48    rearranged from "x < n" to "x+(-n) < 0", which is of course hopelessly
49    wrong if that addition overflows.
50 
51    The workaround here avoids this bug by ensuring n is not a literal
52    constant.  Note that this is alpha specific.  The offending transformation
53    is/was in alpha.c alpha_emit_conditional_branch() under "We want to use
54    cmpcc/bcc".
55 
56    Bizarrely, it turns out this happens also with Cray cc on
57    alphaev5-cray-unicosmk2.0.6.X, and has the same solution.  Don't know why
58    or how.  */
59 
60 #if HAVE_HOST_CPU_FAMILY_alpha                                        \
61   && ((defined (__GNUC__) && ! __GMP_GNUC_PREREQ(3,4))      \
62       || defined (_CRAY))
63 static volatile const long CONST_1024 = 1024;
64 static volatile const long CONST_NEG_1023 = -1023;
65 static volatile const long CONST_NEG_1022_SUB_53 = -1022 - 53;
66 #else
67 #define CONST_1024        (1024)
68 #define CONST_NEG_1023              (-1023)
69 #define CONST_NEG_1022_SUB_53 (-1022 - 53)
70 #endif
71 
72 
73 
74 /* Return the value {ptr,size}*2^exp, and negative if sign<0.
75    Must have size>=1, and a non-zero high limb ptr[size-1].
76 
77    {ptr,size} is truncated towards zero.  This is consistent with other gmp
78    conversions, like mpz_set_f or mpz_set_q, and is easy to implement and
79    test.
80 
81    In the past conversions had attempted (imperfectly) to let the hardware
82    float rounding mode take effect, but that gets tricky since multiple
83    roundings need to be avoided, or taken into account, and denorms mean the
84    effective precision of the mantissa is not constant.  (For reference,
85    mpz_get_d on IEEE systems was ok, except it operated on the absolute
86    value.  mpf_get_d and mpq_get_d suffered from multiple roundings and from
87    not always using enough bits to get the rounding right.)
88 
89    It's felt that GMP is not primarily concerned with hardware floats, and
90    really isn't enhanced by getting involved with hardware rounding modes
91    (which could even be some weird unknown style), so something unambiguous
92    and straightforward is best.
93 
94 
95    The IEEE code below is the usual case, it knows either a 32-bit or 64-bit
96    limb and is done with shifts and masks.  The 64-bit case in particular
97    should come out nice and compact.
98 
99    The generic code works one bit at a time, which will be quite slow, but
100    should support any binary-based "double" and be safe against any rounding
101    mode.  Note in particular it works on IEEE systems too.
102 
103 
104    Traps:
105 
106    Hardware traps for overflow to infinity, underflow to zero, or
107    unsupported denorms may or may not be taken.  The IEEE code works bitwise
108    and so probably won't trigger them, the generic code works by float
109    operations and so probably will.  This difference might be thought less
110    than ideal, but again its felt straightforward code is better than trying
111    to get intimate with hardware exceptions (of perhaps unknown nature).
112 
113 
114    Not done:
115 
116    mpz_get_d in the past handled size==1 with a cast limb->double.  This
117    might still be worthwhile there (for up to the mantissa many bits), but
118    for mpn_get_d here, the cost of applying "exp" to the resulting exponent
119    would probably use up any benefit a cast may have over bit twiddling.
120    Also, if the exponent is pushed into denorm range then bit twiddling is
121    the only option, to ensure the desired truncation is obtained.
122 
123 
124    Other:
125 
126    For reference, note that HPPA 8000, 8200, 8500 and 8600 trap FCNV,UDW,DBL
127    to the kernel for values >= 2^63.  This makes it slow, and worse the kernel
128    Linux (what versions?) apparently uses untested code in its trap handling
129    routines, and gets the sign wrong.  We don't use such a limb-to-double
130    cast, neither in the IEEE or generic code.  */
131 
132 
133 double
mpn_get_d(mp_srcptr up,mp_size_t size,mp_size_t sign,long exp)134 mpn_get_d (mp_srcptr up, mp_size_t size, mp_size_t sign, long exp)
135 {
136   ASSERT (size >= 0);
137   ASSERT_MPN (up, size);
138   ASSERT (size == 0 || up[size-1] != 0);
139 
140   if (size == 0)
141     return 0.0;
142 
143   /* Adjust exp to a radix point just above {up,size}, guarding against
144      overflow.  After this exp can of course be reduced to anywhere within
145      the {up,size} region without underflow.  */
146   if (UNLIKELY ((unsigned long) (GMP_NUMB_BITS * size)
147                     > (unsigned long) (LONG_MAX - exp)))
148     {
149       if (_GMP_IEEE_FLOATS)
150           goto ieee_infinity;
151 
152       /* generic */
153       exp = LONG_MAX;
154     }
155   else
156     {
157       exp += GMP_NUMB_BITS * size;
158     }
159 
160 
161 #if 1
162 {
163   int lshift, nbits;
164   union ieee_double_extract u;
165   mp_limb_t x, mhi, mlo;
166 #if GMP_LIMB_BITS == 64
167   mp_limb_t m;
168   up += size;
169   m = *--up;
170   count_leading_zeros (lshift, m);
171 
172   exp -= (lshift - GMP_NAIL_BITS) + 1;
173   m <<= lshift;
174 
175   nbits = GMP_LIMB_BITS - lshift;
176 
177   if (nbits < 53 && size > 1)
178     {
179       x = *--up;
180       x <<= GMP_NAIL_BITS;
181       x >>= nbits;
182       m |= x;
183       nbits += GMP_NUMB_BITS;
184 
185       if (LIMBS_PER_DOUBLE >= 3 && nbits < 53 && size > 2)
186           {
187             x = *--up;
188             x <<= GMP_NAIL_BITS;
189             x >>= nbits;
190             m |= x;
191             nbits += GMP_NUMB_BITS;
192           }
193     }
194   mhi = m >> (32 + 11);
195   mlo = m >> 11;
196 #endif
197 #if GMP_LIMB_BITS == 32
198   up += size;
199   x = *--up, size--;
200   count_leading_zeros (lshift, x);
201 
202   exp -= (lshift - GMP_NAIL_BITS) + 1;
203   x <<= lshift;
204   mhi = x >> 11;
205 
206   if (lshift < 11)            /* FIXME: never true if NUMB < 20 bits */
207     {
208       /* All 20 bits in mhi */
209       mlo = x << 21;
210       /* >= 1 bit in mlo */
211       nbits = GMP_LIMB_BITS - lshift - 21;
212     }
213   else
214     {
215       if (size != 0)
216           {
217             nbits = GMP_LIMB_BITS - lshift;
218 
219             x = *--up, size--;
220             x <<= GMP_NAIL_BITS;
221             mhi |= x >> nbits >> 11;
222 
223             mlo = x << GMP_LIMB_BITS - nbits - 11;
224             nbits = nbits + 11 - GMP_NAIL_BITS;
225           }
226       else
227           {
228             mlo = 0;
229             goto done;
230           }
231     }
232 
233   if (LIMBS_PER_DOUBLE >= 2 && nbits < 32 && size != 0)
234     {
235       x = *--up, size--;
236       x <<= GMP_NAIL_BITS;
237       x >>= nbits;
238       mlo |= x;
239       nbits += GMP_NUMB_BITS;
240 
241       if (LIMBS_PER_DOUBLE >= 3 && nbits < 32 && size != 0)
242           {
243             x = *--up, size--;
244             x <<= GMP_NAIL_BITS;
245             x >>= nbits;
246             mlo |= x;
247             nbits += GMP_NUMB_BITS;
248 
249             if (LIMBS_PER_DOUBLE >= 4 && nbits < 32 && size != 0)
250               {
251                 x = *--up;
252                 x <<= GMP_NAIL_BITS;
253                 x >>= nbits;
254                 mlo |= x;
255                 nbits += GMP_NUMB_BITS;
256               }
257           }
258     }
259 
260  done:;
261 
262 #endif
263   {
264     if (UNLIKELY (exp >= CONST_1024))
265       {
266           /* overflow, return infinity */
267       ieee_infinity:
268           mhi = 0;
269           mlo = 0;
270           exp = 1024;
271       }
272     else if (UNLIKELY (exp <= CONST_NEG_1023))
273       {
274           int rshift;
275 
276           if (LIKELY (exp <= CONST_NEG_1022_SUB_53))
277             return 0.0;        /* denorm underflows to zero */
278 
279           rshift = -1022 - exp;
280           ASSERT (rshift > 0 && rshift < 53);
281 #if GMP_LIMB_BITS > 53
282           mlo >>= rshift;
283           mhi = mlo >> 32;
284 #else
285           if (rshift >= 32)
286             {
287               mlo = mhi;
288               mhi = 0;
289               rshift -= 32;
290             }
291           lshift = GMP_LIMB_BITS - rshift;
292           mlo = (mlo >> rshift) | (rshift == 0 ? 0 : mhi << lshift);
293           mhi >>= rshift;
294 #endif
295           exp = -1023;
296       }
297   }
298   u.s.manh = mhi;
299   u.s.manl = mlo;
300   u.s.exp = exp + 1023;
301   u.s.sig = (sign < 0);
302   return u.d;
303 }
304 #else
305 
306 
307 #define ONE_LIMB    (GMP_LIMB_BITS == 64 && 2*GMP_NUMB_BITS >= 53)
308 #define TWO_LIMBS   (GMP_LIMB_BITS == 32 && 3*GMP_NUMB_BITS >= 53)
309 
310   if (_GMP_IEEE_FLOATS && (ONE_LIMB || TWO_LIMBS))
311     {
312       union ieee_double_extract          u;
313       mp_limb_t      m0, m1, m2, rmask;
314       int  lshift, rshift;
315 
316       m0 = up[size-1];                                /* high limb */
317       m1 = (size >= 2 ? up[size-2] : 0);   /* second highest limb */
318       count_leading_zeros (lshift, m0);
319 
320       /* relative to just under high non-zero bit */
321       exp -= (lshift - GMP_NAIL_BITS) + 1;
322 
323       if (ONE_LIMB)
324           {
325             /* lshift to have high of m0 non-zero, and collapse nails */
326             rshift = GMP_LIMB_BITS - lshift;
327             m1 <<= GMP_NAIL_BITS;
328             rmask = GMP_NAIL_BITS == 0 && lshift == 0 ? 0 : MP_LIMB_T_MAX;
329             m0 = (m0 << lshift) | ((m1 >> rshift) & rmask);
330 
331             /* rshift back to have bit 53 of m0 the high non-zero */
332             m0 >>= 11;
333           }
334       else /* TWO_LIMBS */
335           {
336             m2 = (size >= 3 ? up[size-3] : 0);  /* third highest limb */
337 
338             /* collapse nails from m1 and m2 */
339 #if GMP_NAIL_BITS != 0
340             m1 = (m1 << GMP_NAIL_BITS) | (m2 >> (GMP_NUMB_BITS-GMP_NAIL_BITS));
341             m2 <<= 2*GMP_NAIL_BITS;
342 #endif
343 
344             /* lshift to have high of m0:m1 non-zero, collapse nails from m0 */
345             rshift = GMP_LIMB_BITS - lshift;
346             rmask = (GMP_NAIL_BITS == 0 && lshift == 0 ? 0 : MP_LIMB_T_MAX);
347             m0 = (m0 << lshift) | ((m1 >> rshift) & rmask);
348             m1 = (m1 << lshift) | ((m2 >> rshift) & rmask);
349 
350             /* rshift back to have bit 53 of m0:m1 the high non-zero */
351             m1 = (m1 >> 11) | (m0 << (GMP_LIMB_BITS-11));
352             m0 >>= 11;
353           }
354 
355       if (UNLIKELY (exp >= CONST_1024))
356           {
357             /* overflow, return infinity */
358           ieee_infinity:
359             m0 = 0;
360             m1 = 0;
361             exp = 1024;
362           }
363       else if (UNLIKELY (exp <= CONST_NEG_1023))
364           {
365             if (LIKELY (exp <= CONST_NEG_1022_SUB_53))
366               return 0.0;      /* denorm underflows to zero */
367 
368             rshift = -1022 - exp;
369             ASSERT (rshift > 0 && rshift < 53);
370             if (ONE_LIMB)
371               {
372                 m0 >>= rshift;
373               }
374             else /* TWO_LIMBS */
375               {
376                 if (rshift >= 32)
377                     {
378                       m1 = m0;
379                       m0 = 0;
380                       rshift -= 32;
381                     }
382                 lshift = GMP_LIMB_BITS - rshift;
383                 m1 = (m1 >> rshift) | (rshift == 0 ? 0 : m0 << lshift);
384                 m0 >>= rshift;
385               }
386             exp = -1023;
387           }
388 
389       if (ONE_LIMB)
390           {
391 #if GMP_LIMB_BITS > 32        /* avoid compiler warning about big shift */
392             u.s.manh = m0 >> 32;
393 #endif
394             u.s.manl = m0;
395           }
396       else /* TWO_LIMBS */
397           {
398             u.s.manh = m0;
399             u.s.manl = m1;
400           }
401 
402       u.s.exp = exp + 1023;
403       u.s.sig = (sign < 0);
404       return u.d;
405     }
406   else
407     {
408       /* Non-IEEE or strange limb size, do something generic. */
409 
410       mp_size_t          i;
411       mp_limb_t          limb, bit;
412       int      shift;
413       double             base, factor, prev_factor, d, new_d, diff;
414 
415       /* "limb" is "up[i]" the limb being examined, "bit" is a mask for the
416            bit being examined, initially the highest non-zero bit.  */
417       i = size-1;
418       limb = up[i];
419       count_leading_zeros (shift, limb);
420       bit = GMP_LIMB_HIGHBIT >> shift;
421 
422       /* relative to just under high non-zero bit */
423       exp -= (shift - GMP_NAIL_BITS) + 1;
424 
425       /* Power up "factor" to 2^exp, being the value of the "bit" in "limb"
426            being examined.  */
427       base = (exp >= 0 ? 2.0 : 0.5);
428       exp = ABS (exp);
429       factor = 1.0;
430       for (;;)
431           {
432             if (exp & 1)
433               {
434                 prev_factor = factor;
435                 factor *= base;
436                 FORCE_DOUBLE (factor);
437                 if (factor == 0.0)
438                     return 0.0;         /* underflow */
439                 if (factor == prev_factor)
440                     {
441                       d = factor;         /* overflow, apparent infinity */
442                       goto generic_done;
443                     }
444               }
445             exp >>= 1;
446             if (exp == 0)
447               break;
448             base *= base;
449           }
450 
451       /* Add a "factor" for each non-zero bit, working from high to low.
452            Stop if any rounding occurs, hence implementing a truncation.
453 
454            Note no attention is paid to DBL_MANT_DIG, since the effective
455            number of bits in the mantissa isn't constant when in denorm range.
456            We also encountered an ARM system with apparently somewhat doubtful
457            software floats where DBL_MANT_DIG claimed 53 bits but only 32
458            actually worked.  */
459 
460       d = factor;  /* high bit */
461       for (;;)
462           {
463             factor *= 0.5;  /* next bit */
464             bit >>= 1;
465             if (bit == 0)
466               {
467                 /* next limb, if any */
468                 i--;
469                 if (i < 0)
470                     break;
471                 limb = up[i];
472                 bit = GMP_NUMB_HIGHBIT;
473               }
474 
475             if (bit & limb)
476               {
477                 new_d = d + factor;
478                 FORCE_DOUBLE (new_d);
479                 diff = new_d - d;
480                 if (diff != factor)
481                     break;     /* rounding occured, stop now */
482                 d = new_d;
483               }
484           }
485 
486     generic_done:
487       return (sign >= 0 ? d : -d);
488     }
489 #endif
490 }
491