Skip to content

Commit cf7c548

Browse files
committed
Strict BigDecimal("0.1e#{exponent}") exponent overflow/underflow check
1 parent 15b7e0a commit cf7c548

3 files changed

Lines changed: 71 additions & 51 deletions

File tree

ext/bigdecimal/bigdecimal.c

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@
3838
#define SIGNED_VALUE_MAX INTPTR_MAX
3939
#define SIGNED_VALUE_MIN INTPTR_MIN
4040
#define MUL_OVERFLOW_SIGNED_VALUE_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, SIGNED_VALUE_MIN, SIGNED_VALUE_MAX)
41+
#define ADD_OVERFLOW_SIGNED_VALUE_P(a, b) ADD_OVERFLOW_SIGNED_INTEGER_P(a, b, SIGNED_VALUE_MIN, SIGNED_VALUE_MAX)
4142

4243
/* max_value = 0.9999_9999_9999E[exponent], exponent <= SIGNED_VALUE_MAX */
4344
#define VP_EXPONENT_MAX (SIGNED_VALUE_MAX / BASE_FIG)
4445
/* min_value = 0.0001_0000_0000E[exponent], exponent-(BASE_FIG-1) >= SIGNED_VALUE_MIN */
4546
#define VP_EXPONENT_MIN ((SIGNED_VALUE_MIN + BASE_FIG - 1) / BASE_FIG)
47+
#define EXPONENT_MAX (VP_EXPONENT_MAX * BASE_FIG)
48+
#define EXPONENT_MIN (VP_EXPONENT_MIN * BASE_FIG - (BASE_FIG - 1))
4649

4750
VALUE rb_cBigDecimal;
4851
VALUE rb_mBigMath;
@@ -6812,7 +6815,7 @@ VP_EXPORT int
68126815
VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size_t nf, const char *exp_chr, size_t ne)
68136816
{
68146817
size_t i, j, ind_a, ma, mi, me;
6815-
SIGNED_VALUE e, es, eb, ef;
6818+
SIGNED_VALUE e;
68166819
int sign, signe, exponent_overflow;
68176820

68186821
/* get exponent part */
@@ -6835,23 +6838,13 @@ VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size_t nf, con
68356838
++me;
68366839
}
68376840
while (i < me) {
6838-
if (MUL_OVERFLOW_SIGNED_VALUE_P(e, (SIGNED_VALUE)BASE_FIG)) {
6839-
es = e;
6840-
goto exp_overflow;
6841-
}
6842-
es = e * (SIGNED_VALUE)BASE_FIG;
6843-
if (MUL_OVERFLOW_SIGNED_VALUE_P(e, 10) ||
6844-
SIGNED_VALUE_MAX - (exp_chr[i] - '0') < e * 10)
6845-
goto exp_overflow;
6846-
e = e * 10 + exp_chr[i] - '0';
6847-
if (MUL_OVERFLOW_SIGNED_VALUE_P(e, (SIGNED_VALUE)BASE_FIG))
6848-
goto exp_overflow;
6849-
if (es > (SIGNED_VALUE)(e * BASE_FIG)) {
6850-
exp_overflow:
6841+
int dig = exp_chr[i] - '0';
6842+
if (MUL_OVERFLOW_SIGNED_VALUE_P(e, 10) ||
6843+
ADD_OVERFLOW_SIGNED_VALUE_P(e * 10, signe * dig)) {
68516844
exponent_overflow = 1;
6852-
e = es; /* keep sign */
68536845
break;
68546846
}
6847+
e = e * 10 + signe * dig;
68556848
++i;
68566849
}
68576850
}
@@ -6870,34 +6863,32 @@ VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size_t nf, con
68706863
++mi;
68716864
}
68726865
}
6866+
/* skip leading zeros in integer part */
6867+
while (i < mi && int_chr[i] == '0') {
6868+
++i;
6869+
--ni;
6870+
}
68736871

6874-
e = signe * e; /* e: The value of exponent part. */
6875-
e = e + ni; /* set actual exponent size. */
6876-
6877-
if (e > 0) signe = 1;
6878-
else signe = -1;
6872+
/* set actual exponent size. */
6873+
if (ADD_OVERFLOW_SIGNED_VALUE_P(e, (long)ni)) {
6874+
exponent_overflow = 1;
6875+
} else {
6876+
e += ni;
6877+
}
68796878

68806879
/* Adjust the exponent so that it is the multiple of BASE_FIG. */
6881-
j = 0;
6882-
ef = 1;
6883-
while (ef) {
6884-
if (e >= 0) eb = e;
6885-
else eb = -e;
6886-
ef = eb / (SIGNED_VALUE)BASE_FIG;
6887-
ef = eb - ef * (SIGNED_VALUE)BASE_FIG;
6888-
if (ef) {
6889-
++j; /* Means to add one more preceding zero */
6890-
++e;
6891-
}
6880+
j = (BASE_FIG - e % BASE_FIG) % BASE_FIG;
6881+
if (ADD_OVERFLOW_SIGNED_VALUE_P(e, (long)j)) {
6882+
exponent_overflow = 1;
6883+
} else {
6884+
e += j;
68926885
}
68936886

6894-
eb = e / (SIGNED_VALUE)BASE_FIG;
6895-
6896-
if (exponent_overflow) {
6887+
if (exponent_overflow || e < EXPONENT_MIN || e > EXPONENT_MAX) {
68976888
int zero = 1;
68986889
for ( ; i < mi && zero; i++) zero = int_chr[i] == '0';
68996890
for (i = 0; i < nf && zero; i++) zero = frac[i] == '0';
6900-
if (!zero && signe > 0) {
6891+
if (!zero && e > 0) {
69016892
VpSetInf(a, sign);
69026893
VpException(VP_EXCEPTION_INFINITY, "exponent overflow",0);
69036894
}
@@ -6947,7 +6938,7 @@ VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size_t nf, con
69476938
++j;
69486939
}
69496940
a->Prec = ind_a + 1;
6950-
a->exponent = eb;
6941+
a->exponent = e / (SIGNED_VALUE)BASE_FIG;
69516942
VpSetSign(a, sign);
69526943
VpNmlz(a);
69536944
return 1;

ext/bigdecimal/bits.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
((b) > 0 ? (max) / (a) < (b) : (min) / (a) > (b)) : \
2727
((b) > 0 ? (min) / (a) < (b) : (max) / (a) > (b)))
2828

29+
#define ADD_OVERFLOW_SIGNED_INTEGER_P(a, b, min, max) ( \
30+
((a) > 0) == ((b) > 0) && ((a) > 0 ? (max) - (a) < (b) : (min) - (a) > (b)))
31+
2932
#ifdef HAVE_UINT128_T
3033
# define bit_length(x) \
3134
(unsigned int) \

test/bigdecimal/test_bigdecimal.rb

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class TestBigDecimal < Test::Unit::TestCase
1515
LLONG_MIN = [LLONG_MAX + 1].pack("Q!").unpack("q!")[0]
1616
ULLONG_MAX = (1 << Fiddle::SIZEOF_LONG_LONG*8) - 1
1717
LIMITS = {
18+
"LONG_MAX" => LONG_MAX,
19+
"LONG_MIN" => LONG_MIN,
1820
"LLONG_MIN" => LLONG_MIN,
1921
"ULLONG_MAX" => ULLONG_MAX,
2022
"FIXNUM_MIN" => LONG_MIN / 2,
@@ -25,6 +27,9 @@ class TestBigDecimal < Test::Unit::TestCase
2527
}.freeze
2628
end
2729

30+
EXPONENT_MAX = LIMITS['LONG_MAX'] / BASE_FIG * BASE_FIG
31+
EXPONENT_MIN = (LIMITS['LONG_MIN'] - 2) / BASE_FIG * BASE_FIG + BASE_FIG + 1
32+
2833
ROUNDING_MODE_MAP = [
2934
[ BigDecimal::ROUND_UP, :up],
3035
[ BigDecimal::ROUND_DOWN, :down],
@@ -80,12 +85,24 @@ def test_BigDecimal
8085
assert_raise(ArgumentError) { BigDecimal("1", -1) }
8186
assert_raise_with_message(ArgumentError, /"1__1_1"/) { BigDecimal("1__1_1") }
8287
assert_raise_with_message(ArgumentError, /"_1_1_1"/) { BigDecimal("_1_1_1") }
88+
assert(BigDecimal("0.1E#{EXPONENT_MAX}").finite?)
89+
assert(BigDecimal("0.1E#{EXPONENT_MIN}").finite?)
8390

8491
BigDecimal.save_exception_mode do
8592
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
93+
BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, false)
8694
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
8795
assert_positive_infinite(BigDecimal("Infinity"))
88-
assert_positive_infinite(BigDecimal("1E1111111111111111111"))
96+
assert_positive_infinite(BigDecimal("0.1E#{EXPONENT_MAX + 1}"))
97+
assert_negative_infinite(BigDecimal("-0.1E#{EXPONENT_MAX + 1}"))
98+
assert_positive_infinite(BigDecimal("1E#{EXPONENT_MAX}"))
99+
assert_negative_infinite(BigDecimal("-1E#{EXPONENT_MAX}"))
100+
assert_positive_zero(BigDecimal("0E#{EXPONENT_MAX + 1}"))
101+
assert_negative_zero(BigDecimal("-0E#{EXPONENT_MAX + 1}"))
102+
assert_positive_zero(BigDecimal("0.1E#{EXPONENT_MIN - 1}"))
103+
assert_negative_zero(BigDecimal("-0.1E#{EXPONENT_MIN - 1}"))
104+
assert_positive_zero(BigDecimal("0.01E#{EXPONENT_MIN}"))
105+
assert_negative_zero(BigDecimal("-0.01E#{EXPONENT_MIN}"))
89106
assert_positive_infinite(BigDecimal(" \t\n\r \rInfinity \t\n\r \r"))
90107
assert_negative_infinite(BigDecimal("-Infinity"))
91108
assert_negative_infinite(BigDecimal(" \t\n\r \r-Infinity \t\n\r \r"))
@@ -460,39 +477,48 @@ def test_exception_overflow
460477
end
461478
end
462479

480+
def test_add_sub_underflow
481+
BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, false)
482+
x = BigDecimal("0.100000000002E#{EXPONENT_MIN + 10}")
483+
y = BigDecimal("0.100000000001E#{EXPONENT_MIN + 10}")
484+
z = BigDecimal("0.101E#{EXPONENT_MIN + 10}")
485+
assert_not_equal(0, x - z)
486+
assert_not_equal(0, z - y)
487+
assert_positive_zero(x + (-y))
488+
assert_positive_zero(x - y)
489+
assert_positive_zero((-y) - (-x))
490+
assert_negative_zero((-x) + y)
491+
assert_negative_zero(y - x)
492+
assert_negative_zero((-x) - (-y))
493+
end
494+
463495
def test_mult_div_overflow_underflow_sign
464496
BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
465497
BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, false)
498+
large_x = BigDecimal("0.1E#{EXPONENT_MAX}")
499+
small_x = BigDecimal("0.1E#{EXPONENT_MIN}")
466500

467-
large_x = BigDecimal("10")
468-
100.times do
469-
x2 = large_x * large_x
470-
break if x2.infinite?
471-
large_x = x2
472-
end
473-
474-
small_x = BigDecimal("0.1")
475-
100.times do
476-
x2 = small_x * small_x
477-
break if x2.zero?
478-
small_x = x2
479-
end
480-
501+
assert_positive_infinite(large_x * 10)
502+
assert_positive_infinite(10 * large_x)
481503
assert_positive_infinite(large_x * large_x)
482504
assert_negative_infinite(large_x * (-large_x))
483505
assert_negative_infinite((-large_x) * large_x)
484506
assert_positive_infinite((-large_x) * (-large_x))
485507

508+
assert_positive_zero(small_x * 0.1)
509+
assert_positive_zero(0.1 * small_x)
486510
assert_positive_zero(small_x * small_x)
487511
assert_negative_zero(small_x * (-small_x))
488512
assert_negative_zero((-small_x) * small_x)
489513
assert_positive_zero((-small_x) * (-small_x))
490514

515+
assert_positive_infinite(large_x.div(0.1, 10))
491516
assert_positive_infinite(large_x.div(small_x, 10))
492517
assert_negative_infinite(large_x.div(-small_x, 10))
493518
assert_negative_infinite((-large_x).div(small_x, 10))
494519
assert_positive_infinite((-large_x).div(-small_x, 10))
495520

521+
assert_positive_zero(small_x.div(10, 10))
496522
assert_positive_zero(small_x.div(large_x, 10))
497523
assert_negative_zero(small_x.div(-large_x, 10))
498524
assert_negative_zero((-small_x).div(large_x, 10))

0 commit comments

Comments
 (0)