[ruby/bigdecimal] Allow digits=0 in BigDecimal(flt) and Float#to_d
Using dtoa of mode=0, we can determine the number of digits in decimal that is necessary to represent the given Float number without errors. This change permits digits=0 in BigDecimal(flt) and Float#to_d, and these methods use dtoa of mode=0 when the given digits is 0. Internal implicit conversion from Float also uses digits=0. [Fix GH-70] https://github.com/ruby/bigdecimal/commit/2dbe170e35
This commit is contained in:
parent
30f1316401
commit
a5b4b806de
@ -947,7 +947,7 @@ BigDecimal_coerce(VALUE self, VALUE other)
|
|||||||
Real *b;
|
Real *b;
|
||||||
|
|
||||||
if (RB_TYPE_P(other, T_FLOAT)) {
|
if (RB_TYPE_P(other, T_FLOAT)) {
|
||||||
GUARD_OBJ(b, GetVpValueWithPrec(other, DBLE_FIG, 1));
|
GUARD_OBJ(b, GetVpValueWithPrec(other, 0, 1));
|
||||||
obj = rb_assoc_new(VpCheckGetValue(b), self);
|
obj = rb_assoc_new(VpCheckGetValue(b), self);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1005,7 +1005,7 @@ BigDecimal_add(VALUE self, VALUE r)
|
|||||||
|
|
||||||
GUARD_OBJ(a, GetVpValue(self, 1));
|
GUARD_OBJ(a, GetVpValue(self, 1));
|
||||||
if (RB_TYPE_P(r, T_FLOAT)) {
|
if (RB_TYPE_P(r, T_FLOAT)) {
|
||||||
b = GetVpValueWithPrec(r, DBLE_FIG, 1);
|
b = GetVpValueWithPrec(r, 0, 1);
|
||||||
}
|
}
|
||||||
else if (RB_TYPE_P(r, T_RATIONAL)) {
|
else if (RB_TYPE_P(r, T_RATIONAL)) {
|
||||||
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
|
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
|
||||||
@ -1063,7 +1063,7 @@ BigDecimal_sub(VALUE self, VALUE r)
|
|||||||
|
|
||||||
GUARD_OBJ(a, GetVpValue(self,1));
|
GUARD_OBJ(a, GetVpValue(self,1));
|
||||||
if (RB_TYPE_P(r, T_FLOAT)) {
|
if (RB_TYPE_P(r, T_FLOAT)) {
|
||||||
b = GetVpValueWithPrec(r, DBLE_FIG, 1);
|
b = GetVpValueWithPrec(r, 0, 1);
|
||||||
}
|
}
|
||||||
else if (RB_TYPE_P(r, T_RATIONAL)) {
|
else if (RB_TYPE_P(r, T_RATIONAL)) {
|
||||||
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
|
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
|
||||||
@ -1113,7 +1113,7 @@ BigDecimalCmp(VALUE self, VALUE r,char op)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case T_FLOAT:
|
case T_FLOAT:
|
||||||
GUARD_OBJ(b, GetVpValueWithPrec(r, DBLE_FIG, 0));
|
GUARD_OBJ(b, GetVpValueWithPrec(r, 0, 0));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_RATIONAL:
|
case T_RATIONAL:
|
||||||
@ -1326,7 +1326,7 @@ BigDecimal_mult(VALUE self, VALUE r)
|
|||||||
|
|
||||||
GUARD_OBJ(a, GetVpValue(self, 1));
|
GUARD_OBJ(a, GetVpValue(self, 1));
|
||||||
if (RB_TYPE_P(r, T_FLOAT)) {
|
if (RB_TYPE_P(r, T_FLOAT)) {
|
||||||
b = GetVpValueWithPrec(r, DBLE_FIG, 1);
|
b = GetVpValueWithPrec(r, 0, 1);
|
||||||
}
|
}
|
||||||
else if (RB_TYPE_P(r, T_RATIONAL)) {
|
else if (RB_TYPE_P(r, T_RATIONAL)) {
|
||||||
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
|
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
|
||||||
@ -1354,7 +1354,7 @@ BigDecimal_divide(Real **c, Real **res, Real **div, VALUE self, VALUE r)
|
|||||||
|
|
||||||
GUARD_OBJ(a, GetVpValue(self, 1));
|
GUARD_OBJ(a, GetVpValue(self, 1));
|
||||||
if (RB_TYPE_P(r, T_FLOAT)) {
|
if (RB_TYPE_P(r, T_FLOAT)) {
|
||||||
b = GetVpValueWithPrec(r, DBLE_FIG, 1);
|
b = GetVpValueWithPrec(r, 0, 1);
|
||||||
}
|
}
|
||||||
else if (RB_TYPE_P(r, T_RATIONAL)) {
|
else if (RB_TYPE_P(r, T_RATIONAL)) {
|
||||||
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
|
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
|
||||||
@ -1420,7 +1420,7 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod)
|
|||||||
|
|
||||||
GUARD_OBJ(a, GetVpValue(self, 1));
|
GUARD_OBJ(a, GetVpValue(self, 1));
|
||||||
if (RB_TYPE_P(r, T_FLOAT)) {
|
if (RB_TYPE_P(r, T_FLOAT)) {
|
||||||
b = GetVpValueWithPrec(r, DBLE_FIG, 1);
|
b = GetVpValueWithPrec(r, 0, 1);
|
||||||
}
|
}
|
||||||
else if (RB_TYPE_P(r, T_RATIONAL)) {
|
else if (RB_TYPE_P(r, T_RATIONAL)) {
|
||||||
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
|
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
|
||||||
@ -1521,7 +1521,7 @@ BigDecimal_divremain(VALUE self, VALUE r, Real **dv, Real **rv)
|
|||||||
|
|
||||||
GUARD_OBJ(a, GetVpValue(self, 1));
|
GUARD_OBJ(a, GetVpValue(self, 1));
|
||||||
if (RB_TYPE_P(r, T_FLOAT)) {
|
if (RB_TYPE_P(r, T_FLOAT)) {
|
||||||
b = GetVpValueWithPrec(r, DBLE_FIG, 1);
|
b = GetVpValueWithPrec(r, 0, 1);
|
||||||
}
|
}
|
||||||
else if (RB_TYPE_P(r, T_RATIONAL)) {
|
else if (RB_TYPE_P(r, T_RATIONAL)) {
|
||||||
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
|
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
|
||||||
@ -2416,7 +2416,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
|
|||||||
if (NIL_P(prec)) {
|
if (NIL_P(prec)) {
|
||||||
n += DBLE_FIG;
|
n += DBLE_FIG;
|
||||||
}
|
}
|
||||||
exp = GetVpValueWithPrec(vexp, DBLE_FIG, 1);
|
exp = GetVpValueWithPrec(vexp, 0, 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_RATIONAL:
|
case T_RATIONAL:
|
||||||
@ -2829,7 +2829,8 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
|
|||||||
char buf[DBLE_FIG + BASE_FIG + 2 + 1];
|
char buf[DBLE_FIG + BASE_FIG + 2 + 1];
|
||||||
int decpt, negative_p;
|
int decpt, negative_p;
|
||||||
char *e;
|
char *e;
|
||||||
char *p = BigDecimal_dtoa(d, 2, digs, &decpt, &negative_p, &e);
|
const int mode = digs == 0 ? 0 : 2;
|
||||||
|
char *p = BigDecimal_dtoa(d, mode, digs, &decpt, &negative_p, &e);
|
||||||
int len10 = (int)(e - p);
|
int len10 = (int)(e - p);
|
||||||
if (len10 >= (int)sizeof(buf))
|
if (len10 >= (int)sizeof(buf))
|
||||||
len10 = (int)sizeof(buf) - 1;
|
len10 = (int)sizeof(buf) - 1;
|
||||||
@ -3006,6 +3007,7 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
|
|||||||
|
|
||||||
VALUE copy = TypedData_Wrap_Struct(rb_cBigDecimal, &BigDecimal_data_type, 0);
|
VALUE copy = TypedData_Wrap_Struct(rb_cBigDecimal, &BigDecimal_data_type, 0);
|
||||||
vp = VpCopy(NULL, vp);
|
vp = VpCopy(NULL, vp);
|
||||||
|
/* TODO: rounding */
|
||||||
BigDecimal_wrap_struct(copy, vp);
|
BigDecimal_wrap_struct(copy, vp);
|
||||||
return VpCheckGetValue(vp);
|
return VpCheckGetValue(vp);
|
||||||
}
|
}
|
||||||
@ -3046,19 +3048,28 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* call-seq:
|
/* call-seq:
|
||||||
* BigDecimal(initial, digits=0, exception: true)
|
* BigDecimal(arg, exception: true)
|
||||||
|
* BigDecimal(arg, digits, exception: true)
|
||||||
*
|
*
|
||||||
* Create a new BigDecimal object.
|
* Returns <i>arg</i> converted to a BigDecimal. Numeric types are converted
|
||||||
|
* directly. Other types except for String are first converted to String
|
||||||
|
* by <code>to_str</code>. Strings can be converted when it has appropriate
|
||||||
|
* forms of decimal numbers. Exceptions can be suppressed by passing
|
||||||
|
* <code>exception: false</code>.
|
||||||
*
|
*
|
||||||
* initial:: The initial value, as an Integer, a Float, a Rational,
|
* When <i>arg</i> is a Float and <i>digits</i> is <code>0</code>, the number
|
||||||
* a BigDecimal, or a String.
|
* of digits is determined by the algorithm of <code>dtoa</code> function
|
||||||
|
* written by David M. Gay. That algorithm is based on "How to Print Floating-
|
||||||
|
* Point Numbers Accurately" by Guy L. Steele, Jr. and Jon L. White [Proc. ACM
|
||||||
|
* SIGPLAN '90, pp. 112-126].
|
||||||
|
*
|
||||||
|
* arg:: The value converted to a BigDecimal.
|
||||||
*
|
*
|
||||||
* If it is a String, spaces are ignored and unrecognized characters
|
* If it is a String, spaces are ignored and unrecognized characters
|
||||||
* terminate the value.
|
* terminate the value.
|
||||||
*
|
*
|
||||||
* digits:: The number of significant digits, as an Integer. If omitted or 0,
|
* digits:: The number of significant digits, as an Integer. If omitted,
|
||||||
* the number of significant digits is determined from the initial
|
* the number of significant digits is determined from <i>arg</i>.
|
||||||
* value.
|
|
||||||
*
|
*
|
||||||
* The actual number of significant digits used in computation is
|
* The actual number of significant digits used in computation is
|
||||||
* usually larger than the specified number.
|
* usually larger than the specified number.
|
||||||
@ -3303,7 +3314,7 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec)
|
|||||||
infinite = isinf(flo);
|
infinite = isinf(flo);
|
||||||
nan = isnan(flo);
|
nan = isnan(flo);
|
||||||
if (!infinite && !nan) {
|
if (!infinite && !nan) {
|
||||||
vx = GetVpValueWithPrec(x, DBLE_FIG, 0);
|
vx = GetVpValueWithPrec(x, 0, 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -3456,7 +3467,7 @@ get_vp_value:
|
|||||||
infinite = isinf(flo);
|
infinite = isinf(flo);
|
||||||
nan = isnan(flo);
|
nan = isnan(flo);
|
||||||
if (!zero && !negative && !infinite && !nan) {
|
if (!zero && !negative && !infinite && !nan) {
|
||||||
vx = GetVpValueWithPrec(x, DBLE_FIG, 1);
|
vx = GetVpValueWithPrec(x, 0, 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class Float < Numeric
|
|||||||
#
|
#
|
||||||
# See also BigDecimal::new.
|
# See also BigDecimal::new.
|
||||||
#
|
#
|
||||||
def to_d(precision=Float::DIG+1)
|
def to_d(precision=0)
|
||||||
BigDecimal(self, precision)
|
BigDecimal(self, precision)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -917,6 +917,7 @@ class TestBigDecimal < Test::Unit::TestCase
|
|||||||
|
|
||||||
def test_mult_with_float
|
def test_mult_with_float
|
||||||
assert_kind_of(BigDecimal, BigDecimal("3") * 1.5)
|
assert_kind_of(BigDecimal, BigDecimal("3") * 1.5)
|
||||||
|
assert_equal(BigDecimal("64.4"), BigDecimal(1) * 64.4)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_mult_with_rational
|
def test_mult_with_rational
|
||||||
@ -955,6 +956,7 @@ class TestBigDecimal < Test::Unit::TestCase
|
|||||||
|
|
||||||
def test_div_with_float
|
def test_div_with_float
|
||||||
assert_kind_of(BigDecimal, BigDecimal("3") / 1.5)
|
assert_kind_of(BigDecimal, BigDecimal("3") / 1.5)
|
||||||
|
assert_equal(BigDecimal("0.5"), BigDecimal(1) / 2.0)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_div_with_rational
|
def test_div_with_rational
|
||||||
|
@ -19,11 +19,11 @@ class TestBigDecimalUtil < Test::Unit::TestCase
|
|||||||
|
|
||||||
def test_Float_to_d_without_precision
|
def test_Float_to_d_without_precision
|
||||||
delta = 1.0/10**(Float::DIG+1)
|
delta = 1.0/10**(Float::DIG+1)
|
||||||
assert_in_delta(BigDecimal(0.5, Float::DIG+1), 0.5.to_d, delta)
|
assert_in_delta(BigDecimal(0.5, 0), 0.5.to_d, delta)
|
||||||
assert_in_delta(BigDecimal(355.0/113.0, Float::DIG+1), (355.0/113.0).to_d, delta)
|
assert_in_delta(BigDecimal(355.0/113.0, 0), (355.0/113.0).to_d, delta)
|
||||||
|
|
||||||
assert_equal(9.05, 9.05.to_d.to_f)
|
assert_equal(9.05, 9.05.to_d.to_f)
|
||||||
assert_equal("9.050000000000001", 9.05.to_d.to_s('F'))
|
assert_equal("9.05", 9.05.to_d.to_s('F'))
|
||||||
|
|
||||||
assert_equal(Math::PI, Math::PI.to_d.to_f)
|
assert_equal(Math::PI, Math::PI.to_d.to_f)
|
||||||
|
|
||||||
@ -34,6 +34,8 @@ class TestBigDecimalUtil < Test::Unit::TestCase
|
|||||||
assert_raise(TypeError) { 0.3.to_d(false) }
|
assert_raise(TypeError) { 0.3.to_d(false) }
|
||||||
|
|
||||||
assert(1.1.to_d.frozen?)
|
assert(1.1.to_d.frozen?)
|
||||||
|
|
||||||
|
assert_equal(BigDecimal("999_999.9999"), 999_999.9999.to_d)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_Float_to_d_with_precision
|
def test_Float_to_d_with_precision
|
||||||
|
Loading…
x
Reference in New Issue
Block a user