Skip to content

Commit 2dbe170

Browse files
committed
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]
1 parent d071a0a commit 2dbe170

File tree

4 files changed

+40
-25
lines changed

4 files changed

+40
-25
lines changed

ext/bigdecimal/bigdecimal.c

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -947,7 +947,7 @@ BigDecimal_coerce(VALUE self, VALUE other)
947947
Real *b;
948948

949949
if (RB_TYPE_P(other, T_FLOAT)) {
950-
GUARD_OBJ(b, GetVpValueWithPrec(other, DBLE_FIG, 1));
950+
GUARD_OBJ(b, GetVpValueWithPrec(other, 0, 1));
951951
obj = rb_assoc_new(VpCheckGetValue(b), self);
952952
}
953953
else {
@@ -1005,7 +1005,7 @@ BigDecimal_add(VALUE self, VALUE r)
10051005

10061006
GUARD_OBJ(a, GetVpValue(self, 1));
10071007
if (RB_TYPE_P(r, T_FLOAT)) {
1008-
b = GetVpValueWithPrec(r, DBLE_FIG, 1);
1008+
b = GetVpValueWithPrec(r, 0, 1);
10091009
}
10101010
else if (RB_TYPE_P(r, T_RATIONAL)) {
10111011
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
@@ -1063,7 +1063,7 @@ BigDecimal_sub(VALUE self, VALUE r)
10631063

10641064
GUARD_OBJ(a, GetVpValue(self,1));
10651065
if (RB_TYPE_P(r, T_FLOAT)) {
1066-
b = GetVpValueWithPrec(r, DBLE_FIG, 1);
1066+
b = GetVpValueWithPrec(r, 0, 1);
10671067
}
10681068
else if (RB_TYPE_P(r, T_RATIONAL)) {
10691069
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
@@ -1113,7 +1113,7 @@ BigDecimalCmp(VALUE self, VALUE r,char op)
11131113
break;
11141114

11151115
case T_FLOAT:
1116-
GUARD_OBJ(b, GetVpValueWithPrec(r, DBLE_FIG, 0));
1116+
GUARD_OBJ(b, GetVpValueWithPrec(r, 0, 0));
11171117
break;
11181118

11191119
case T_RATIONAL:
@@ -1326,7 +1326,7 @@ BigDecimal_mult(VALUE self, VALUE r)
13261326

13271327
GUARD_OBJ(a, GetVpValue(self, 1));
13281328
if (RB_TYPE_P(r, T_FLOAT)) {
1329-
b = GetVpValueWithPrec(r, DBLE_FIG, 1);
1329+
b = GetVpValueWithPrec(r, 0, 1);
13301330
}
13311331
else if (RB_TYPE_P(r, T_RATIONAL)) {
13321332
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
@@ -1354,7 +1354,7 @@ BigDecimal_divide(Real **c, Real **res, Real **div, VALUE self, VALUE r)
13541354

13551355
GUARD_OBJ(a, GetVpValue(self, 1));
13561356
if (RB_TYPE_P(r, T_FLOAT)) {
1357-
b = GetVpValueWithPrec(r, DBLE_FIG, 1);
1357+
b = GetVpValueWithPrec(r, 0, 1);
13581358
}
13591359
else if (RB_TYPE_P(r, T_RATIONAL)) {
13601360
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
@@ -1420,7 +1420,7 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod)
14201420

14211421
GUARD_OBJ(a, GetVpValue(self, 1));
14221422
if (RB_TYPE_P(r, T_FLOAT)) {
1423-
b = GetVpValueWithPrec(r, DBLE_FIG, 1);
1423+
b = GetVpValueWithPrec(r, 0, 1);
14241424
}
14251425
else if (RB_TYPE_P(r, T_RATIONAL)) {
14261426
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
@@ -1521,7 +1521,7 @@ BigDecimal_divremain(VALUE self, VALUE r, Real **dv, Real **rv)
15211521

15221522
GUARD_OBJ(a, GetVpValue(self, 1));
15231523
if (RB_TYPE_P(r, T_FLOAT)) {
1524-
b = GetVpValueWithPrec(r, DBLE_FIG, 1);
1524+
b = GetVpValueWithPrec(r, 0, 1);
15251525
}
15261526
else if (RB_TYPE_P(r, T_RATIONAL)) {
15271527
b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
@@ -2416,7 +2416,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
24162416
if (NIL_P(prec)) {
24172417
n += DBLE_FIG;
24182418
}
2419-
exp = GetVpValueWithPrec(vexp, DBLE_FIG, 1);
2419+
exp = GetVpValueWithPrec(vexp, 0, 1);
24202420
break;
24212421

24222422
case T_RATIONAL:
@@ -2832,7 +2832,8 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
28322832
char buf[DBLE_FIG + BASE_FIG + 2 + 1];
28332833
int decpt, negative_p;
28342834
char *e;
2835-
char *p = BigDecimal_dtoa(d, 2, digs, &decpt, &negative_p, &e);
2835+
const int mode = digs == 0 ? 0 : 2;
2836+
char *p = BigDecimal_dtoa(d, mode, digs, &decpt, &negative_p, &e);
28362837
int len10 = (int)(e - p);
28372838
if (len10 >= (int)sizeof(buf))
28382839
len10 = (int)sizeof(buf) - 1;
@@ -3009,6 +3010,7 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
30093010

30103011
VALUE copy = TypedData_Wrap_Struct(rb_cBigDecimal, &BigDecimal_data_type, 0);
30113012
vp = VpCopy(NULL, vp);
3013+
/* TODO: rounding */
30123014
BigDecimal_wrap_struct(copy, vp);
30133015
return VpCheckGetValue(vp);
30143016
}
@@ -3049,19 +3051,28 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
30493051
}
30503052

30513053
/* call-seq:
3052-
* BigDecimal(initial, digits=0, exception: true)
3054+
* BigDecimal(arg, exception: true)
3055+
* BigDecimal(arg, digits, exception: true)
30533056
*
3054-
* Create a new BigDecimal object.
3057+
* Returns <i>arg</i> converted to a BigDecimal. Numeric types are converted
3058+
* directly. Other types except for String are first converted to String
3059+
* by <code>to_str</code>. Strings can be converted when it has appropriate
3060+
* forms of decimal numbers. Exceptions can be suppressed by passing
3061+
* <code>exception: false</code>.
30553062
*
3056-
* initial:: The initial value, as an Integer, a Float, a Rational,
3057-
* a BigDecimal, or a String.
3063+
* When <i>arg</i> is a Float and <i>digits</i> is <code>0</code>, the number
3064+
* of digits is determined by the algorithm of <code>dtoa</code> function
3065+
* written by David M. Gay. That algorithm is based on "How to Print Floating-
3066+
* Point Numbers Accurately" by Guy L. Steele, Jr. and Jon L. White [Proc. ACM
3067+
* SIGPLAN '90, pp. 112-126].
30583068
*
3059-
* If it is a String, spaces are ignored and unrecognized characters
3060-
* terminate the value.
3069+
* arg:: The value converted to a BigDecimal.
30613070
*
3062-
* digits:: The number of significant digits, as an Integer. If omitted or 0,
3063-
* the number of significant digits is determined from the initial
3064-
* value.
3071+
* If it is a String, spaces are ignored and unrecognized characters
3072+
* terminate the value.
3073+
*
3074+
* digits:: The number of significant digits, as an Integer. If omitted,
3075+
* the number of significant digits is determined from <i>arg</i>.
30653076
*
30663077
* The actual number of significant digits used in computation is
30673078
* usually larger than the specified number.
@@ -3306,7 +3317,7 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec)
33063317
infinite = isinf(flo);
33073318
nan = isnan(flo);
33083319
if (!infinite && !nan) {
3309-
vx = GetVpValueWithPrec(x, DBLE_FIG, 0);
3320+
vx = GetVpValueWithPrec(x, 0, 0);
33103321
}
33113322
break;
33123323

@@ -3459,7 +3470,7 @@ BigMath_s_log(VALUE klass, VALUE x, VALUE vprec)
34593470
infinite = isinf(flo);
34603471
nan = isnan(flo);
34613472
if (!zero && !negative && !infinite && !nan) {
3462-
vx = GetVpValueWithPrec(x, DBLE_FIG, 1);
3473+
vx = GetVpValueWithPrec(x, 0, 1);
34633474
}
34643475
break;
34653476

lib/bigdecimal/util.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class Float < Numeric
4343
#
4444
# See also BigDecimal::new.
4545
#
46-
def to_d(precision=Float::DIG+1)
46+
def to_d(precision=0)
4747
BigDecimal(self, precision)
4848
end
4949
end

test/bigdecimal/test_bigdecimal.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,7 @@ def test_mult
915915

916916
def test_mult_with_float
917917
assert_kind_of(BigDecimal, BigDecimal("3") * 1.5)
918+
assert_equal(BigDecimal("64.4"), BigDecimal(1) * 64.4)
918919
end
919920

920921
def test_mult_with_rational
@@ -953,6 +954,7 @@ def test_div
953954

954955
def test_div_with_float
955956
assert_kind_of(BigDecimal, BigDecimal("3") / 1.5)
957+
assert_equal(BigDecimal("0.5"), BigDecimal(1) / 2.0)
956958
end
957959

958960
def test_div_with_rational

test/bigdecimal/test_bigdecimal_util.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ def test_Integer_to_d
1919

2020
def test_Float_to_d_without_precision
2121
delta = 1.0/10**(Float::DIG+1)
22-
assert_in_delta(BigDecimal(0.5, Float::DIG+1), 0.5.to_d, delta)
23-
assert_in_delta(BigDecimal(355.0/113.0, Float::DIG+1), (355.0/113.0).to_d, delta)
22+
assert_in_delta(BigDecimal(0.5, 0), 0.5.to_d, delta)
23+
assert_in_delta(BigDecimal(355.0/113.0, 0), (355.0/113.0).to_d, delta)
2424

2525
assert_equal(9.05, 9.05.to_d.to_f)
26-
assert_equal("9.050000000000001", 9.05.to_d.to_s('F'))
26+
assert_equal("9.05", 9.05.to_d.to_s('F'))
2727

2828
assert_equal(Math::PI, Math::PI.to_d.to_f)
2929

@@ -34,6 +34,8 @@ def test_Float_to_d_without_precision
3434
assert_raise(TypeError) { 0.3.to_d(false) }
3535

3636
assert(1.1.to_d.frozen?)
37+
38+
assert_equal(BigDecimal("999_999.9999"), 999_999.9999.to_d)
3739
end
3840

3941
def test_Float_to_d_with_precision

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy