diff options
author | zimmerma <zimmerma@280ebfd0-de03-0410-8827-d642c229c3f4> | 2018-06-20 14:10:04 +0000 |
---|---|---|
committer | zimmerma <zimmerma@280ebfd0-de03-0410-8827-d642c229c3f4> | 2018-06-20 14:10:04 +0000 |
commit | 0af8867dcafb9299f63d9a0947a981facee5165c (patch) | |
tree | 6d3317f93702ddeb854b6bbd8c51919cf886c146 /src/get_d128.c | |
parent | 673568cc583d7a0a1b7103ecefedab0f1bda1aa7 (diff) | |
download | mpfr-0af8867dcafb9299f63d9a0947a981facee5165c.tar.gz |
added mpfr_get_decimal128 (still experimental)
git-svn-id: svn://scm.gforge.inria.fr/svn/mpfr/trunk@12795 280ebfd0-de03-0410-8827-d642c229c3f4
Diffstat (limited to 'src/get_d128.c')
-rw-r--r-- | src/get_d128.c | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/src/get_d128.c b/src/get_d128.c new file mode 100644 index 000000000..d01f7dba2 --- /dev/null +++ b/src/get_d128.c @@ -0,0 +1,440 @@ +/* mpfr_get_decimal128 -- convert a multiple precision floating-point number + to an IEEE 754-2008 decimal128 float + +See https://gcc.gnu.org/ml/gcc/2006-06/msg00691.html, +https://gcc.gnu.org/onlinedocs/gcc/Decimal-Float.html, +and TR 24732 <http://www.open-std.org/jtc1/sc22/wg14/www/projects#24732>. + +Copyright 2006-2018 Free Software Foundation, Inc. +Contributed by the AriC and Caramba projects, INRIA. + +This file is part of the GNU MPFR Library. + +The GNU MPFR Library is free software; you can redistribute it and/or modify +it under the terms of 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. + +The GNU MPFR 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 Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see +http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include "mpfr-impl.h" +#include "ieee_floats.h" + +#define ISDIGIT(c) ('0' <= c && c <= '9') + +#ifdef MPFR_WANT_DECIMAL_FLOATS + +#ifndef DEC128_MAX +# define DEC128_MAX 9.999999999999999999999999999999999E6144dl +#endif + +/* construct a decimal128 NaN */ +static _Decimal128 +get_decimal128_nan (void) +{ + return (_Decimal128) MPFR_DBL_NAN; +} + +/* construct the decimal128 Inf with given sign */ +static _Decimal128 +get_decimal128_inf (int negative) +{ + return (_Decimal128) (negative ? MPFR_DBL_INFM : MPFR_DBL_INFP); +} + +/* construct the decimal128 zero with given sign */ +static _Decimal128 +get_decimal128_zero (int negative) +{ + _Decimal128 zero = 0; + return (_Decimal128) (negative ? -zero : zero); +} + +/* construct the decimal128 smallest non-zero with given sign: + it is 10^emin * 10^(1-p). Since emax = 6144, emin = 1-emax = -6143, + and p = 34, we get 10^(-6176) */ +static _Decimal128 +get_decimal128_min (int negative) +{ + return negative ? - 1E-6176dl : 1E-6176dl; +} + +/* construct the decimal128 largest finite number with given sign */ +static _Decimal128 +get_decimal128_max (int negative) +{ + return negative ? - DEC128_MAX : DEC128_MAX; +} + +/* one-to-one conversion: + s is a decimal string representing a number x = m * 10^e which must be + exactly representable in the decimal128 format, i.e. + (a) the mantissa m has at most 34 decimal digits + (b1) -6143 <= e <= 6144 with m integer multiple of 10^(-33), |m| < 10 + (b2) or -6176 <= e <= 6111 with m integer, |m| < 10^34. + Assumes s is neither NaN nor +Inf nor -Inf. + s = [-][0-9]+E[-][0-9]+ + + The decimal128 format (cf table 3.6 of IEEE 754-2008) has the following + parameters: + * k = 128 (number of bits of storage) + * p = 34 (precision in digits) + * emax = 6144 + * bias = E-q = 6176 + * sign bit has 1 bit + * w+5 = 17 bits (combination field width) + * t = 110 bits (trailing significand width) + We have k = 1 + 5 + w + t = 128. +*/ +static _Decimal128 +string_to_Decimal128 (char *s) /* portable version */ +{ + long int exp = 0; + char m[35]; + long n = 0; /* mantissa length */ + char *endptr[1]; + _Decimal128 x = 0.0dl; + int sign = 0; + + /* read sign */ + if (*s == '-') + { + sign = 1; + s ++; + } + /* read mantissa */ + while (ISDIGIT (*s)) + m[n++] = *s++; + + /* as constructed in mpfr_get_decimal128, s cannot have any '.' separator */ + + /* we will consider an integer mantissa m*10^exp */ + MPFR_ASSERTN(n <= 34); + /* s always has an exponent separator 'E' */ + MPFR_ASSERTN(*s == 'E'); + exp = strtol (s + 1, endptr, 10); + MPFR_ASSERTN(**endptr == '\0'); + MPFR_ASSERTN(-6176 <= exp && exp <= (long) (6145 - n)); + while (n < 34) + { + m[n++] = '0'; + exp --; + } + /* now n=34 and -6176 <= exp <= 6111, cf (b2) */ + m[n] = '\0'; + + /* the number to convert is m[] * 10^exp where the mantissa is a 34-digit + integer */ + + /* compute biased exponent */ + exp += 6176; + + MPFR_ASSERTN(exp >= -33); + if (exp < 0) + { + int i; + n = -exp; + /* check the last n digits of the mantissa are zero */ + for (i = 1; i <= n; i++) + MPFR_ASSERTN(m[34 - n] == '0'); + /* shift the first (34-n) digits to the right */ + for (i = 34 - n - 1; i >= 0; i--) + m[i + n] = m[i]; + /* zero the first n digits */ + for (i = 0; i < n; i ++) + m[i] = '0'; + exp = 0; + } + + /* the number to convert is m[] * 10^(exp-6176) */ + exp -= 6176; + + for (n = 0; n < 34; n++) + x = (_Decimal128) 10.0 * x + (_Decimal128) (m[n] - '0'); + + /* multiply by 10^exp */ + if (exp > 0) + { + _Decimal128 ten = 10; + _Decimal128 ten2 = ten * ten; + _Decimal128 ten4 = ten2 * ten2; + _Decimal128 ten8 = ten4 * ten4; + _Decimal128 ten16 = ten8 * ten8; + _Decimal128 ten32 = ten16 * ten16; + _Decimal128 ten64 = ten32 * ten32; + _Decimal128 ten128 = ten64 * ten64; + _Decimal128 ten256 = ten128 * ten128; + _Decimal128 ten512 = ten256 * ten256; + _Decimal128 ten1024 = ten512 * ten512; + _Decimal128 ten2048 = ten1024 * ten1024; + _Decimal128 ten4096 = ten2048 * ten2048; + + if (exp >= 4096) + { + x *= ten4096; + exp -= 4096; + } + if (exp >= 2048) + { + x *= ten2048; + exp -= 2048; + } + if (exp >= 1024) + { + x *= ten1024; + exp -= 1024; + } + if (exp >= 512) + { + x *= ten512; + exp -= 512; + } + if (exp >= 256) + { + x *= ten256; + exp -= 256; + } + if (exp >= 128) + { + x *= ten128; + exp -= 128; + } + if (exp >= 64) + { + x *= ten64; + exp -= 64; + } + if (exp >= 32) + { + x *= ten32; + exp -= 32; + } + if (exp >= 16) + { + x *= ten16; + exp -= 16; + } + if (exp >= 8) + { + x *= ten8; + exp -= 8; + } + if (exp >= 4) + { + x *= ten4; + exp -= 4; + } + if (exp >= 2) + { + x *= ten2; + exp -= 2; + } + if (exp >= 1) + { + x *= ten; + exp -= 1; + } + } + else if (exp < 0) + { + _Decimal128 ten = 10; + _Decimal128 ten2 = ten * ten; + _Decimal128 ten4 = ten2 * ten2; + _Decimal128 ten8 = ten4 * ten4; + _Decimal128 ten16 = ten8 * ten8; + _Decimal128 ten32 = ten16 * ten16; + _Decimal128 ten64 = ten32 * ten32; + _Decimal128 ten128 = ten64 * ten64; + _Decimal128 ten256 = ten128 * ten128; + _Decimal128 ten512 = ten256 * ten256; + _Decimal128 ten1024 = ten512 * ten512; + _Decimal128 ten2048 = ten1024 * ten1024; + _Decimal128 ten4096 = ten2048 * ten2048; + + if (exp <= -4096) + { + x /= ten4096; + exp += 4096; + } + if (exp <= -2048) + { + x /= ten2048; + exp += 2048; + } + if (exp <= -1024) + { + x /= ten1024; + exp += 1024; + } + if (exp <= -512) + { + x /= ten512; + exp += 512; + } + if (exp <= -256) + { + x /= ten256; + exp += 256; + } + if (exp <= -128) + { + x /= ten128; + exp += 128; + } + if (exp <= -64) + { + x /= ten64; + exp += 64; + } + if (exp <= -32) + { + x /= ten32; + exp += 32; + } + if (exp <= -16) + { + x /= (_Decimal128) 10000000000000000.0; + exp += 16; + } + if (exp <= -8) + { + x /= (_Decimal128) 100000000.0; + exp += 8; + } + if (exp <= -4) + { + x /= (_Decimal128) 10000.0; + exp += 4; + } + if (exp <= -2) + { + x /= (_Decimal128) 100.0; + exp += 2; + } + if (exp <= -1) + { + x /= (_Decimal128) 10.0; + exp += 1; + } + } + + if (sign) + x = -x; + + return x; +} + +_Decimal128 +mpfr_get_decimal128 (mpfr_srcptr src, mpfr_rnd_t rnd_mode) +{ + int negative; + mpfr_exp_t e; + + /* the encoding of NaN, Inf, zero is the same under DPD or BID */ + if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (src))) + { + if (MPFR_IS_NAN (src)) + return get_decimal128_nan (); + + negative = MPFR_IS_NEG (src); + + if (MPFR_IS_INF (src)) + return get_decimal128_inf (negative); + + MPFR_ASSERTD (MPFR_IS_ZERO(src)); + return get_decimal128_zero (negative); + } + + e = MPFR_GET_EXP (src); + negative = MPFR_IS_NEG (src); + + MPFR_UPDATE2_RND_MODE (rnd_mode, MPFR_SIGN (src)); + + /* now rnd_mode is RNDN, RNDF, RNDA or RNDZ */ + + /* the smallest decimal128 number is 10^(-6176), + with 2^(-20517) < 10^(-6176) < 2^(-20516) */ + if (MPFR_UNLIKELY (e < -20517)) /* src <= 2^(-20518) < 1/2*10^(-6176) */ + { + if (rnd_mode != MPFR_RNDA) + return get_decimal128_zero (negative); + else /* RNDA: return the smallest non-zero number */ + return get_decimal128_min (negative); + } + /* the largest decimal128 number is just below 10^6145 < 2^20414 */ + else if (MPFR_UNLIKELY (e > 20414)) /* then src >= 2^20414 */ + { + if (rnd_mode == MPFR_RNDZ) + return get_decimal128_max (negative); + else /* RNDN, RNDA, RNDF: round away */ + return get_decimal128_inf (negative); + } + else + { + /* we need to store the sign (1), the mantissa (34), and the terminating + character, thus we need at least 35 characters in s */ + char s[36]; + mpfr_get_str (s, &e, 10, 34, src, rnd_mode); + /* the smallest normal number is 1.000...000E-6143, + which corresponds to s=[0.]1000...000 and e=-6142 */ + if (e < -6142) + { + /* the smallest subnormal number is 0.000...001E-6143 = 1E-6176, + which corresponds to s=[0.]1000...000 and e=-6175 */ + if (e < -6175) + { + if (rnd_mode == MPFR_RNDN && e == -6176) + { + /* If 0.5E-6176 < |src| < 1E-6176 (smallest subnormal), + src should round to +/- 1E-6176 in MPFR_RNDN. */ + mpfr_get_str (s, &e, 10, 1, src, MPFR_RNDA); + return e == -6176 && s[negative] <= '5' ? + get_decimal128_zero (negative) : + get_decimal128_min (negative); + } + if (rnd_mode == MPFR_RNDZ || rnd_mode == MPFR_RNDN) + return get_decimal128_zero (negative); + else /* RNDA or RNDF: return the smallest non-zero number */ + return get_decimal128_min (negative); + } + else + { + mpfr_exp_t e2; + long digits = 34 - (-6142 - e); + /* if e = -6175 then 34 - (-6142 - e) = 1 */ + mpfr_get_str (s, &e2, 10, digits, src, rnd_mode); + /* Warning: we can have e2 = e + 1 here, when rounding to + nearest or away from zero. */ + s[negative + digits] = 'E'; + sprintf (s + negative + digits + 1, "%ld", + (long int)e2 - digits); + return string_to_Decimal128 (s); + } + } + /* the largest number is 9.999...999E+6144, + which corresponds to s=[0.]9999...999 and e=6145 */ + else if (e > 6145) + { + if (rnd_mode == MPFR_RNDZ) + return get_decimal128_max (negative); + else /* RNDN, RNDA, RNDF: round away */ + return get_decimal128_inf (negative); + } + else /* -6142 <= e <= 6145 */ + { + s[34 + negative] = 'E'; + sprintf (s + 35 + negative, "%ld", (long int) e - 34); + return string_to_Decimal128 (s); + } + } +} + +#endif /* MPFR_WANT_DECIMAL_FLOATS */ |