diff options
author | Kevin B Kenny <kennykb@acm.org> | 2011-07-02 21:56:33 (GMT) |
---|---|---|
committer | Kevin B Kenny <kennykb@acm.org> | 2011-07-02 21:56:33 (GMT) |
commit | 59f60c354a264bd149bbe3248982f82c6ddd165a (patch) | |
tree | 32125ee4763fe4e82d3a6f93e806e3328c1fade5 /generic | |
parent | ab9198a4a8bfe2f03879c8fc4ae46b5f8eb64d60 (diff) | |
download | tcl-59f60c354a264bd149bbe3248982f82c6ddd165a.zip tcl-59f60c354a264bd149bbe3248982f82c6ddd165a.tar.gz tcl-59f60c354a264bd149bbe3248982f82c6ddd165a.tar.bz2 |
Fix roundoff gaffe in bignum-to-double conversion [Bug 3349507]
Diffstat (limited to 'generic')
-rwxr-xr-x | generic/tclStrToD.c | 58 | ||||
-rw-r--r-- | generic/tclStubInit.c | 1 | ||||
-rw-r--r-- | generic/tclTomMath.decls | 3 | ||||
-rw-r--r-- | generic/tclTomMathDecls.h | 11 |
4 files changed, 63 insertions, 10 deletions
diff --git a/generic/tclStrToD.c b/generic/tclStrToD.c index 421657c..7e76727 100755 --- a/generic/tclStrToD.c +++ b/generic/tclStrToD.c @@ -4542,12 +4542,13 @@ TclBignumToDouble( mp_int *a) /* Integer to convert. */ { mp_int b; - int bits, shift, i; + int bits, shift, i, lsb; double r; + /* - * Determine how many bits we need, and extract that many from the input. - * Round to nearest unit in the last place. + * We need a 'mantBits'-bit significand. Determine what shift will + * give us that. */ bits = mp_count_bits(a); @@ -4559,17 +4560,54 @@ TclBignumToDouble( return -HUGE_VAL; } } - shift = mantBits + 1 - bits; + shift = mantBits - bits; + + /* + * If shift > 0, shift the significand left by the requisite number of + * bits. If shift == 0, the significand is already exactly 'mantBits' + * in length. If shift < 0, we will need to shift the significand right + * by the requisite number of bits, and round it. If the '1-shift' + * least significant bits are 0, but the 'shift'th bit is nonzero, + * then the significand lies exactly between two values and must be + * 'rounded to even'. + */ + mp_init(&b); - if (shift > 0) { + if (shift == 0) { + mp_copy(a, &b); + } else if (shift > 0) { mp_mul_2d(a, shift, &b); } else if (shift < 0) { - mp_div_2d(a, -shift, &b, NULL); - } else { - mp_copy(a, &b); + lsb = mp_cnt_lsb(a); + if (lsb == -1-shift) { + + /* + * Round to even + */ + + mp_div_2d(a, -shift, &b, NULL); + if (mp_isodd(&b)) { + if (b.sign == MP_ZPOS) { + mp_add_d(&b, 1, &b); + } else { + mp_sub_d(&b, 1, &b); + } + } + } else { + + /* + * Ordinary rounding + */ + + mp_div_2d(a, -1-shift, &b, NULL); + if (b.sign == MP_ZPOS) { + mp_add_d(&b, 1, &b); + } else { + mp_sub_d(&b, 1, &b); + } + mp_div_2d(&b, 1, &b, NULL); + } } - mp_add_d(&b, 1, &b); - mp_div_2d(&b, 1, &b, NULL); /* * Accumulate the result, one mp_digit at a time. diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index bc29ee6..d8a300c 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -481,6 +481,7 @@ TclTomMathStubs tclTomMathStubs = { TclBN_s_mp_sub, /* 60 */ TclBN_mp_init_set_int, /* 61 */ TclBN_mp_set_int, /* 62 */ + TclBN_mp_cnt_lsb, /* 63 */ }; static TclStubHooks tclStubHooks = { diff --git a/generic/tclTomMath.decls b/generic/tclTomMath.decls index ca883f6..191312f 100644 --- a/generic/tclTomMath.decls +++ b/generic/tclTomMath.decls @@ -217,3 +217,6 @@ declare 61 { declare 62 { int TclBN_mp_set_int(mp_int* a, unsigned long i) } +declare 63 { + int TclBN_mp_cnt_lsb(mp_int* a) +} diff --git a/generic/tclTomMathDecls.h b/generic/tclTomMathDecls.h index f072311..4d5515b 100644 --- a/generic/tclTomMathDecls.h +++ b/generic/tclTomMathDecls.h @@ -63,6 +63,7 @@ #define mp_cmp TclBN_mp_cmp #define mp_cmp_d TclBN_mp_cmp_d #define mp_cmp_mag TclBN_mp_cmp_mag +#define mp_cnt_lsb TclBN_mp_cnt_lsb #define mp_copy TclBN_mp_copy #define mp_count_bits TclBN_mp_count_bits #define mp_div TclBN_mp_div @@ -461,6 +462,11 @@ EXTERN int TclBN_mp_init_set_int(mp_int*a, unsigned long i); /* 62 */ EXTERN int TclBN_mp_set_int(mp_int*a, unsigned long i); #endif +#ifndef TclBN_mp_cnt_lsb_TCL_DECLARED +#define TclBN_mp_cnt_lsb_TCL_DECLARED +/* 63 */ +EXTERN int TclBN_mp_cnt_lsb(mp_int*a); +#endif typedef struct TclTomMathStubs { int magic; @@ -529,6 +535,7 @@ typedef struct TclTomMathStubs { int (*tclBN_s_mp_sub) (mp_int *a, mp_int *b, mp_int *c); /* 60 */ int (*tclBN_mp_init_set_int) (mp_int*a, unsigned long i); /* 61 */ int (*tclBN_mp_set_int) (mp_int*a, unsigned long i); /* 62 */ + int (*tclBN_mp_cnt_lsb) (mp_int*a); /* 63 */ } TclTomMathStubs; #ifdef __cplusplus @@ -797,6 +804,10 @@ extern TclTomMathStubs *tclTomMathStubsPtr; #define TclBN_mp_set_int \ (tclTomMathStubsPtr->tclBN_mp_set_int) /* 62 */ #endif +#ifndef TclBN_mp_cnt_lsb +#define TclBN_mp_cnt_lsb \ + (tclTomMathStubsPtr->tclBN_mp_cnt_lsb) /* 63 */ +#endif #endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */ |