From d7ca01a73ad5287f9d063bbdf5b35f472affda9d Mon Sep 17 00:00:00 2001 From: nobu Date: Thu, 30 Aug 2012 07:17:56 +0000 Subject: [PATCH] bigdecimal.c: check underflow * ext/bigdecimal/bigdecimal.c (BigDecimal_to_f): check underflow since strtod() sets errno to ERANGE at underflow too. [ruby-core:47342] [Bug #6944] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36854 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 6 ++++++ ext/bigdecimal/bigdecimal.c | 6 ++++-- test/bigdecimal/test_bigdecimal.rb | 32 ++++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index 55b791bf66..1d396d38f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Thu Aug 30 16:17:52 2012 Nobuyoshi Nakada + + * ext/bigdecimal/bigdecimal.c (BigDecimal_to_f): check underflow since + strtod() sets errno to ERANGE at underflow too. [ruby-core:47342] + [Bug #6944] + Thu Aug 30 12:44:43 2012 Akinori MUSHA * lib/set.rb (Set#{<,>,<=,>=}): Define comparison operators as diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 866ce345fc..e8ba40a531 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -696,8 +696,10 @@ BigDecimal_to_f(VALUE self) VpToString(p, buf, 0, 0); errno = 0; d = strtod(buf, 0); - if (errno == ERANGE) - goto overflow; + if (errno == ERANGE) { + if (d == 0.0) goto underflow; + if (fabs(d) >= HUGE_VAL) goto overflow; + } return rb_float_new(d); overflow: diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index d440e4cedb..6694697e0e 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -540,15 +540,35 @@ class TestBigDecimal < Test::Unit::TestCase assert_kind_of(Float, x .to_f) assert_kind_of(Float, (-x).to_f) + bug6944 = '[ruby-core:47342]' + BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, true) - assert_raise(FloatDomainError) { - BigDecimal("1e#{Float::MIN_10_EXP - 2*Float::DIG}").to_f } - assert_raise(FloatDomainError) { - BigDecimal("-1e#{Float::MIN_10_EXP - 2*Float::DIG}").to_f } + x = "1e#{Float::MIN_10_EXP - 2*Float::DIG}" + assert_raise(FloatDomainError, x) {BigDecimal(x).to_f} + x = "-#{x}" + assert_raise(FloatDomainError, x) {BigDecimal(x).to_f} + x = "1e#{Float::MIN_10_EXP - Float::DIG}" + assert_nothing_raised(FloatDomainError, x) { + assert_in_delta(0.0, BigDecimal(x).to_f, 10**Float::MIN_10_EXP, bug6944) + } + x = "-#{x}" + assert_nothing_raised(FloatDomainError, x) { + assert_in_delta(0.0, BigDecimal(x).to_f, 10**Float::MIN_10_EXP, bug6944) + } BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, false) - assert_equal( 0.0, BigDecimal("1e#{Float::MIN_10_EXP - 2*Float::DIG}").to_f) - assert_equal(-0.0, BigDecimal("-1e#{Float::MIN_10_EXP - 2*Float::DIG}").to_f) + x = "1e#{Float::MIN_10_EXP - 2*Float::DIG}" + assert_equal( 0.0, BigDecimal(x).to_f, x) + x = "-#{x}" + assert_equal(-0.0, BigDecimal(x).to_f, x) + x = "1e#{Float::MIN_10_EXP - Float::DIG}" + assert_nothing_raised(FloatDomainError, x) { + assert_in_delta(0.0, BigDecimal(x).to_f, 10**Float::MIN_10_EXP, bug6944) + } + x = "-#{x}" + assert_nothing_raised(FloatDomainError, x) { + assert_in_delta(0.0, BigDecimal(x).to_f, 10**Float::MIN_10_EXP, bug6944) + } end def test_coerce