summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
authorKevin B Kenny <kennykb@acm.org>2011-07-02 22:36:45 (GMT)
committerKevin B Kenny <kennykb@acm.org>2011-07-02 22:36:45 (GMT)
commitab7a6af8cae45f25825b7c3ac24edc9af86c9233 (patch)
treecfb063e941efc61b9d4aaf5f1b223dae502e9b76 /generic
parent8f8741ac3a12f301cee5f84fd38210ef0527106c (diff)
parent59f60c354a264bd149bbe3248982f82c6ddd165a (diff)
downloadtcl-ab7a6af8cae45f25825b7c3ac24edc9af86c9233.zip
tcl-ab7a6af8cae45f25825b7c3ac24edc9af86c9233.tar.gz
tcl-ab7a6af8cae45f25825b7c3ac24edc9af86c9233.tar.bz2
Fix roundoff gaffe in bignum-to-double conversion [Bug 3349507]
Diffstat (limited to 'generic')
-rwxr-xr-xgeneric/tclStrToD.c58
-rw-r--r--generic/tclStubInit.c1
-rw-r--r--generic/tclTomMath.decls3
-rw-r--r--generic/tclTomMathDecls.h6
4 files changed, 58 insertions, 10 deletions
diff --git a/generic/tclStrToD.c b/generic/tclStrToD.c
index 16f11d2..15bff3e 100755
--- a/generic/tclStrToD.c
+++ b/generic/tclStrToD.c
@@ -4563,12 +4563,13 @@ TclBignumToDouble(
const 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);
@@ -4580,17 +4581,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 054ece5..5b47e95 100644
--- a/generic/tclStubInit.c
+++ b/generic/tclStubInit.c
@@ -464,6 +464,7 @@ const TclTomMathStubs tclTomMathStubs = {
TclBN_s_mp_sub, /* 60 */
TclBN_mp_init_set_int, /* 61 */
TclBN_mp_set_int, /* 62 */
+ TclBN_mp_cnt_lsb, /* 63 */
};
static const TclStubHooks tclStubHooks = {
diff --git a/generic/tclTomMath.decls b/generic/tclTomMath.decls
index 5bf338e..29a6a03 100644
--- a/generic/tclTomMath.decls
+++ b/generic/tclTomMath.decls
@@ -218,3 +218,6 @@ declare 61 {
declare 62 {
int TclBN_mp_set_int(mp_int* a, unsigned long i)
}
+declare 63 {
+ int TclBN_mp_cnt_lsb(const mp_int* a)
+}
diff --git a/generic/tclTomMathDecls.h b/generic/tclTomMathDecls.h
index 7df0d90..feaefb3 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
@@ -272,6 +273,8 @@ EXTERN int TclBN_s_mp_sub(mp_int *a, mp_int *b, mp_int *c);
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);
+/* 63 */
+EXTERN int TclBN_mp_cnt_lsb(const mp_int*a);
typedef struct TclTomMathStubs {
int magic;
@@ -340,6 +343,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) (const mp_int*a); /* 63 */
} TclTomMathStubs;
#ifdef __cplusplus
@@ -482,6 +486,8 @@ extern const TclTomMathStubs *tclTomMathStubsPtr;
(tclTomMathStubsPtr->tclBN_mp_init_set_int) /* 61 */
#define TclBN_mp_set_int \
(tclTomMathStubsPtr->tclBN_mp_set_int) /* 62 */
+#define TclBN_mp_cnt_lsb \
+ (tclTomMathStubsPtr->tclBN_mp_cnt_lsb) /* 63 */
#endif /* defined(USE_TCL_STUBS) */