diff options
author | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2011-09-01 16:07:16 +0000 |
---|---|---|
committer | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2011-09-01 16:07:16 +0000 |
commit | c4eb2983bac76f60e0d3fe958b74237e2aac90d6 (patch) | |
tree | 296f74363982b68c768e526785d23509c5779993 | |
parent | 43284b6bf8324a762808537d5cd2c0774b662a84 (diff) |
* numeric.c (flo_round): substitute machine dependent magic number.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@33158 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | numeric.c | 11 | ||||
-rw-r--r-- | test/ruby/test_float.rb | 13 |
3 files changed, 23 insertions, 5 deletions
@@ -1,3 +1,7 @@ +Fri Sep 2 01:07:14 2011 Nobuyoshi Nakada <[email protected]> + + * numeric.c (flo_round): substitute machine dependent magic number. + Thu Sep 1 17:31:22 2011 Nobuyoshi Nakada <[email protected]> * insns.def (defineclass), vm_insnhelper.c (vm_get_cvar_base): see @@ -1493,17 +1493,18 @@ flo_round(int argc, VALUE *argv, VALUE num) int ndigits = 0; int binexp; long val; + enum {float_dig = DBL_DIG+2}; if (argc > 0 && rb_scan_args(argc, argv, "01", &nd) == 1) { ndigits = NUM2INT(nd); } number = RFLOAT_VALUE(num); - frexp (number , &binexp); + frexp(number, &binexp); /* Let `exp` be such that `number` is written as:"0.#{digits}e#{exp}", i.e. such that 10 ** (exp - 1) <= |number| < 10 ** exp - Recall that up to 17 digits can be needed to represent a double, - so if ndigits + exp >= 17, the intermediate value (number * 10 ** ndigits) + Recall that up to float_dig digits can be needed to represent a double, + so if ndigits + exp >= float_dig, the intermediate value (number * 10 ** ndigits) will be an integer and thus the result is the original number. If ndigits + exp <= 0, the result is 0 or "1e#{exp}", so if ndigits + exp < 0, the result is 0. @@ -1514,7 +1515,7 @@ flo_round(int argc, VALUE *argv, VALUE num) 10 ** (binexp/4 - 1) < |number| < 10 ** (binexp/3) binexp/4 <= exp <= binexp/3 If binexp <= 0, swap the /4 and the /3 - So if ndigits + binexp/(4 or 3) >= 17, the result is number + So if ndigits + binexp/(4 or 3) >= float_dig, the result is number If ndigits + binexp/(3 or 4) < 0 the result is 0 */ if (isinf(number) || isnan(number)) { @@ -1523,7 +1524,7 @@ flo_round(int argc, VALUE *argv, VALUE num) else if ((long)ndigits * (4 - (binexp > 0)) + binexp < 0) { number = 0; } - else if (((long)ndigits - 17) * (3 + (binexp > 0)) + binexp < 0) { + else if (((long)ndigits - float_dig) * (3 + (binexp > 0)) + binexp < 0) { f = pow(10, abs(ndigits)); if (ndigits < 0) { double absnum = fabs(number); diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb index fb37d73831..c244447bd7 100644 --- a/test/ruby/test_float.rb +++ b/test/ruby/test_float.rb @@ -315,7 +315,9 @@ class TestFloat < Test::Unit::TestCase assert_raise(FloatDomainError) { inf.ceil } assert_raise(FloatDomainError) { inf.round } assert_raise(FloatDomainError) { inf.truncate } + end + def test_round_with_precision assert_equal(1.100, 1.111.round(1)) assert_equal(1.110, 1.111.round(2)) assert_equal(11110.0, 11111.1.round(-1)) @@ -323,6 +325,17 @@ class TestFloat < Test::Unit::TestCase assert_equal(10**300, 1.1e300.round(-300)) assert_equal(-10**300, -1.1e300.round(-300)) + assert_equal(1.0e-300, 1.1e-300.round(300)) + assert_equal(-1.0e-300, -1.1e-300.round(300)) + + bug5227 = '[ruby-core:39093]' + assert_equal(42.0, 42.0.round(308), bug5227) + assert_equal(1.0e307, 1.0e307.round(2), bug5227) + + assert_raise(TypeError) {1.0.round("4")} + assert_raise(TypeError) {1.0.round(nil)} + def (prec = Object.new).to_int; 2; end + assert_equal(1.0, 0.998.round(prec)) end VS = [ |