| /* mpn_get_d -- limbs to double conversion. |
| |
| THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY. THEY'RE ALMOST |
| CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN |
| FUTURE GNU MP RELEASES. |
| |
| Copyright 2003, 2004, 2007, 2009, 2010, 2012 Free Software Foundation, Inc. |
| |
| This file is part of the GNU MP Library. |
| |
| The GNU MP Library is free software; you can redistribute it and/or modify |
| it under the terms of either: |
| |
| * the GNU Lesser General Public License as published by the Free |
| Software Foundation; either version 3 of the License, or (at your |
| option) any later version. |
| |
| or |
| |
| * the GNU General Public License as published by the Free Software |
| Foundation; either version 2 of the License, or (at your option) any |
| later version. |
| |
| or both in parallel, as here. |
| |
| The GNU MP Library is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received copies of the GNU General Public License and the |
| GNU Lesser General Public License along with the GNU MP Library. If not, |
| see https://www.gnu.org/licenses/. */ |
| |
| #include "gmp.h" |
| #include "gmp-impl.h" |
| #include "longlong.h" |
| |
| #ifndef _GMP_IEEE_FLOATS |
| #define _GMP_IEEE_FLOATS 0 |
| #endif |
| |
| /* To force use of the generic C code for testing, put |
| "#define _GMP_IEEE_FLOATS 0" at this point. */ |
| |
| |
| /* In alpha gcc prior to 3.4, signed DI comparisons involving constants are |
| rearranged from "x < n" to "x+(-n) < 0", which is of course hopelessly |
| wrong if that addition overflows. |
| |
| The workaround here avoids this bug by ensuring n is not a literal constant. |
| Note that this is alpha specific. The offending transformation is/was in |
| alpha.c alpha_emit_conditional_branch() under "We want to use cmpcc/bcc". |
| |
| Bizarrely, this happens also with Cray cc on alphaev5-cray-unicosmk2.0.6.X, |
| and has the same solution. Don't know why or how. */ |
| |
| #if HAVE_HOST_CPU_FAMILY_alpha \ |
| && ((defined (__GNUC__) && ! __GMP_GNUC_PREREQ(3,4)) \ |
| || defined (_CRAY)) |
| static volatile const long CONST_1024 = 1024; |
| static volatile const long CONST_NEG_1023 = -1023; |
| static volatile const long CONST_NEG_1022_SUB_53 = -1022 - 53; |
| #else |
| #define CONST_1024 (1024) |
| #define CONST_NEG_1023 (-1023) |
| #define CONST_NEG_1022_SUB_53 (-1022 - 53) |
| #endif |
| |
| |
| /* Return the value {ptr,size}*2^exp, and negative if sign<0. Must have |
| size>=1, and a non-zero high limb ptr[size-1]. |
| |
| When we know the fp format, the result is truncated towards zero. This is |
| consistent with other gmp conversions, like mpz_set_f or mpz_set_q, and is |
| easy to implement and test. |
| |
| When we do not know the format, such truncation seems much harder. One |
| would need to defeat any rounding mode, including round-up. |
| |
| It's felt that GMP is not primarily concerned with hardware floats, and |
| really isn't enhanced by getting involved with hardware rounding modes |
| (which could even be some weird unknown style), so something unambiguous and |
| straightforward is best. |
| |
| |
| The IEEE code below is the usual case, it knows either a 32-bit or 64-bit |
| limb and is done with shifts and masks. The 64-bit case in particular |
| should come out nice and compact. |
| |
| The generic code used to work one bit at a time, which was not only slow, |
| but implicitly relied upon denorms for intermediates, since the lowest bits' |
| weight of a perfectly valid fp number underflows in non-denorm. Therefore, |
| the generic code now works limb-per-limb, initially creating a number x such |
| that 1 <= x <= BASE. (BASE is reached only as result of rounding.) Then |
| x's exponent is scaled with explicit code (not ldexp to avoid libm |
| dependency). It is a tap-dance to avoid underflow or overflow, beware! |
| |
| |
| Traps: |
| |
| Hardware traps for overflow to infinity, underflow to zero, or unsupported |
| denorms may or may not be taken. The IEEE code works bitwise and so |
| probably won't trigger them, the generic code works by float operations and |
| so probably will. This difference might be thought less than ideal, but |
| again its felt straightforward code is better than trying to get intimate |
| with hardware exceptions (of perhaps unknown nature). |
| |
| |
| Not done: |
| |
| mpz_get_d in the past handled size==1 with a cast limb->double. This might |
| still be worthwhile there (for up to the mantissa many bits), but for |
| mpn_get_d here, the cost of applying "exp" to the resulting exponent would |
| probably use up any benefit a cast may have over bit twiddling. Also, if |
| the exponent is pushed into denorm range then bit twiddling is the only |
| option, to ensure the desired truncation is obtained. |
| |
| |
| Other: |
| |
| For reference, note that HPPA 8000, 8200, 8500 and 8600 trap FCNV,UDW,DBL |
| to the kernel for values >= 2^63. This makes it slow, and worse the kernel |
| Linux (what versions?) apparently uses untested code in its trap handling |
| routines, and gets the sign wrong. We don't use such a limb-to-double |
| cast, neither in the IEEE or generic code. */ |
| |
| |
| |
| #undef FORMAT_RECOGNIZED |
| |
| double |
| mpn_get_d (mp_srcptr up, mp_size_t size, mp_size_t sign, long exp) |
| { |
| int lshift, nbits; |
| mp_limb_t x, mhi, mlo; |
| |
| ASSERT (size >= 0); |
| ASSERT_MPN (up, size); |
| ASSERT (size == 0 || up[size-1] != 0); |
| |
| if (size == 0) |
| return 0.0; |
| |
| /* Adjust exp to a radix point just above {up,size}, guarding against |
| overflow. After this exp can of course be reduced to anywhere within |
| the {up,size} region without underflow. */ |
| if (UNLIKELY ((unsigned long) (GMP_NUMB_BITS * size) |
| > ((unsigned long) LONG_MAX - exp))) |
| { |
| #if _GMP_IEEE_FLOATS |
| goto ieee_infinity; |
| #endif |
| |
| /* generic */ |
| exp = LONG_MAX; |
| } |
| else |
| { |
| exp += GMP_NUMB_BITS * size; |
| } |
| |
| #if _GMP_IEEE_FLOATS |
| { |
| union ieee_double_extract u; |
| |
| up += size; |
| |
| #if GMP_LIMB_BITS == 64 |
| mlo = up[-1]; |
| count_leading_zeros (lshift, mlo); |
| |
| exp -= (lshift - GMP_NAIL_BITS) + 1; |
| mlo <<= lshift; |
| |
| nbits = GMP_LIMB_BITS - lshift; |
| |
| if (nbits < 53 && size > 1) |
| { |
| x = up[-2]; |
| x <<= GMP_NAIL_BITS; |
| x >>= nbits; |
| mlo |= x; |
| nbits += GMP_NUMB_BITS; |
| |
| if (LIMBS_PER_DOUBLE >= 3 && nbits < 53 && size > 2) |
| { |
| x = up[-3]; |
| x <<= GMP_NAIL_BITS; |
| x >>= nbits; |
| mlo |= x; |
| nbits += GMP_NUMB_BITS; |
| } |
| } |
| mhi = mlo >> (32 + 11); |
| mlo = mlo >> 11; /* later implicitly truncated to 32 bits */ |
| #endif |
| #if GMP_LIMB_BITS == 32 |
| x = *--up; |
| count_leading_zeros (lshift, x); |
| |
| exp -= (lshift - GMP_NAIL_BITS) + 1; |
| x <<= lshift; |
| mhi = x >> 11; |
| |
| if (lshift < 11) /* FIXME: never true if NUMB < 20 bits */ |
| { |
| /* All 20 bits in mhi */ |
| mlo = x << 21; |
| /* >= 1 bit in mlo */ |
| nbits = GMP_LIMB_BITS - lshift - 21; |
| } |
| else |
| { |
| if (size > 1) |
| { |
| nbits = GMP_LIMB_BITS - lshift; |
| |
| x = *--up, size--; |
| x <<= GMP_NAIL_BITS; |
| mhi |= x >> nbits >> 11; |
| |
| mlo = x << GMP_LIMB_BITS - nbits - 11; |
| nbits = nbits + 11 - GMP_NAIL_BITS; |
| } |
| else |
| { |
| mlo = 0; |
| goto done; |
| } |
| } |
| |
| /* Now all needed bits in mhi have been accumulated. Add bits to mlo. */ |
| |
| if (LIMBS_PER_DOUBLE >= 2 && nbits < 32 && size > 1) |
| { |
| x = up[-1]; |
| x <<= GMP_NAIL_BITS; |
| x >>= nbits; |
| mlo |= x; |
| nbits += GMP_NUMB_BITS; |
| |
| if (LIMBS_PER_DOUBLE >= 3 && nbits < 32 && size > 2) |
| { |
| x = up[-2]; |
| x <<= GMP_NAIL_BITS; |
| x >>= nbits; |
| mlo |= x; |
| nbits += GMP_NUMB_BITS; |
| |
| if (LIMBS_PER_DOUBLE >= 4 && nbits < 32 && size > 3) |
| { |
| x = up[-3]; |
| x <<= GMP_NAIL_BITS; |
| x >>= nbits; |
| mlo |= x; |
| nbits += GMP_NUMB_BITS; |
| } |
| } |
| } |
| |
| done:; |
| |
| #endif |
| if (UNLIKELY (exp >= CONST_1024)) |
| { |
| /* overflow, return infinity */ |
| ieee_infinity: |
| mhi = 0; |
| mlo = 0; |
| exp = 1024; |
| } |
| else if (UNLIKELY (exp <= CONST_NEG_1023)) |
| { |
| int rshift; |
| |
| if (LIKELY (exp <= CONST_NEG_1022_SUB_53)) |
| return 0.0; /* denorm underflows to zero */ |
| |
| rshift = -1022 - exp; |
| ASSERT (rshift > 0 && rshift < 53); |
| #if GMP_LIMB_BITS > 53 |
| mlo >>= rshift; |
| mhi = mlo >> 32; |
| #else |
| if (rshift >= 32) |
| { |
| mlo = mhi; |
| mhi = 0; |
| rshift -= 32; |
| } |
| lshift = GMP_LIMB_BITS - rshift; |
| mlo = (mlo >> rshift) | (rshift == 0 ? 0 : mhi << lshift); |
| mhi >>= rshift; |
| #endif |
| exp = -1023; |
| } |
| u.s.manh = mhi; |
| u.s.manl = mlo; |
| u.s.exp = exp + 1023; |
| u.s.sig = (sign < 0); |
| return u.d; |
| } |
| #define FORMAT_RECOGNIZED 1 |
| #endif |
| |
| #if HAVE_DOUBLE_VAX_D |
| { |
| union double_extract u; |
| |
| up += size; |
| |
| mhi = up[-1]; |
| |
| count_leading_zeros (lshift, mhi); |
| exp -= lshift; |
| mhi <<= lshift; |
| |
| mlo = 0; |
| if (size > 1) |
| { |
| mlo = up[-2]; |
| if (lshift != 0) |
| mhi += mlo >> (GMP_LIMB_BITS - lshift); |
| mlo <<= lshift; |
| |
| if (size > 2 && lshift > 8) |
| { |
| x = up[-3]; |
| mlo += x >> (GMP_LIMB_BITS - lshift); |
| } |
| } |
| |
| if (UNLIKELY (exp >= 128)) |
| { |
| /* overflow, return maximum number */ |
| mhi = 0xffffffff; |
| mlo = 0xffffffff; |
| exp = 127; |
| } |
| else if (UNLIKELY (exp < -128)) |
| { |
| return 0.0; /* underflows to zero */ |
| } |
| |
| u.s.man3 = mhi >> 24; /* drop msb, since implicit */ |
| u.s.man2 = mhi >> 8; |
| u.s.man1 = (mhi << 8) + (mlo >> 24); |
| u.s.man0 = mlo >> 8; |
| u.s.exp = exp + 128; |
| u.s.sig = sign < 0; |
| return u.d; |
| } |
| #define FORMAT_RECOGNIZED 1 |
| #endif |
| |
| #if ! FORMAT_RECOGNIZED |
| { /* Non-IEEE or strange limb size, do something generic. */ |
| mp_size_t i; |
| double d, weight; |
| unsigned long uexp; |
| |
| /* First generate an fp number disregarding exp, instead keeping things |
| within the numb base factor from 1, which should prevent overflow and |
| underflow even for the most exponent limited fp formats. The |
| termination criteria should be refined, since we now include too many |
| limbs. */ |
| weight = 1/MP_BASE_AS_DOUBLE; |
| d = up[size - 1]; |
| for (i = size - 2; i >= 0; i--) |
| { |
| d += up[i] * weight; |
| weight /= MP_BASE_AS_DOUBLE; |
| if (weight == 0) |
| break; |
| } |
| |
| /* Now apply exp. */ |
| exp -= GMP_NUMB_BITS; |
| if (exp > 0) |
| { |
| weight = 2.0; |
| uexp = exp; |
| } |
| else |
| { |
| weight = 0.5; |
| uexp = 1 - (unsigned long) (exp + 1); |
| } |
| #if 1 |
| /* Square-and-multiply exponentiation. */ |
| if (uexp & 1) |
| d *= weight; |
| while (uexp >>= 1) |
| { |
| weight *= weight; |
| if (uexp & 1) |
| d *= weight; |
| } |
| #else |
| /* Plain exponentiation. */ |
| while (uexp > 0) |
| { |
| d *= weight; |
| uexp--; |
| } |
| #endif |
| |
| return sign >= 0 ? d : -d; |
| } |
| #endif |
| } |