ruby-decimal 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +8 -1
- data/README.txt +19 -3
- data/lib/decimal/decimal.rb +53 -16
- data/lib/decimal/version.rb +1 -1
- data/test/test_define_conversions.rb +30 -1
- data/test/test_round.rb +5 -0
- metadata +2 -2
data/History.txt
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
* New functions implemented:
|
8
8
|
exp(), ln(), log10(), power().
|
9
9
|
|
10
|
-
|
10
|
+
== 0.2.1 2009-06-23
|
11
11
|
|
12
12
|
* Bug fixes:
|
13
13
|
- Decimal#inspect was always producing debug information; now it uses $DEBUG
|
@@ -22,3 +22,10 @@
|
|
22
22
|
- setting context exponent limits with elimit
|
23
23
|
- require decimal/shortcut to use D for Decimal
|
24
24
|
|
25
|
+
== 0.2.2 2009-07-21
|
26
|
+
|
27
|
+
* Bug fixes:
|
28
|
+
- Decimal#normal? was incorrect for special values and zero.
|
29
|
+
- After a local context, the global context was set to a copy of the original
|
30
|
+
context, so any previously stored reference to it was now unbound.
|
31
|
+
- Context#normalize was incorrect.
|
data/README.txt
CHANGED
@@ -120,6 +120,14 @@ Decimal::ExtendedContext in the following examples:
|
|
120
120
|
|
121
121
|
Decimal.context = Decimal::ExtendedContext
|
122
122
|
|
123
|
+
Most decimal operations can be executed by using either Context or Decimal methods:
|
124
|
+
|
125
|
+
puts Decimal.context.exp(1) -> 2.71828183
|
126
|
+
puts Decimal(1).exp -> 2.71828183
|
127
|
+
|
128
|
+
If using Context methods, values are automatically converted as if the Decimal() constructor
|
129
|
+
was used.
|
130
|
+
|
123
131
|
==Rounding
|
124
132
|
|
125
133
|
Results are normally rounded using the precision (number of significant digits)
|
@@ -241,14 +249,22 @@ Or with a generic method:
|
|
241
249
|
puts Decimal('1.1').convert_to(Integer) -> 1
|
242
250
|
puts Decimal('1.1').convert_to(Rational) -> 11/10
|
243
251
|
|
244
|
-
|
252
|
+
Thera are also GDAS style conversion operations:
|
253
|
+
|
254
|
+
puts Decimal('1.1').to_integral_value -> 1
|
255
|
+
|
256
|
+
And conversion is also possible to Float:
|
245
257
|
puts Decimal('1.1').to_f -> 1.1
|
246
258
|
puts Decimal('1.1').convert_to(Float) -> 1.1
|
247
259
|
puts Float(Decimal('1.1')) -> 1.1
|
248
260
|
|
249
|
-
|
261
|
+
Types with predefined bidirectional conversion (Integer and Rational)
|
262
|
+
can be operated with Decimal on either side of an operator, and the result will be a Decimal.
|
263
|
+
For Float there is no predefined bidirectional conversion (see below how to define it)
|
264
|
+
and the result of an operation between Decimal and Float will be of type Float.
|
250
265
|
|
251
|
-
puts Decimal(
|
266
|
+
puts (Decimal(1.1) + 2.0).class -> Float
|
267
|
+
puts (2.0 + Decimal(1.1)).class -> Float
|
252
268
|
|
253
269
|
The conversion system is extensible. For example, we can include BigDecimal into it
|
254
270
|
by defining suitable conversion procedures:
|
data/lib/decimal/decimal.rb
CHANGED
@@ -548,7 +548,7 @@ class Decimal
|
|
548
548
|
# normalizes so that the coefficient has precision digits
|
549
549
|
# (this is not the old GDA normalize function)
|
550
550
|
def normalize(x)
|
551
|
-
_convert(x).
|
551
|
+
_convert(x).normalize(self)
|
552
552
|
end
|
553
553
|
|
554
554
|
# Adjusted exponent of x returned as a Decimal value.
|
@@ -799,20 +799,22 @@ class Decimal
|
|
799
799
|
# of the particular rounding rules used.
|
800
800
|
def strict_epsilon(sign=+1)
|
801
801
|
return exception(InvalidOperation, "Exact context strict epsilon") if exact?
|
802
|
-
coeff = 1
|
803
|
-
exp = 1-precision
|
804
802
|
# assume radix is even (Decimal.radix%2 == 0)
|
805
803
|
case rounding
|
806
804
|
when :down, :floor
|
805
|
+
# largest epsilon: 0.0...10 (precision digits shown to the right of the decimal point)
|
807
806
|
exp = 1-precision
|
808
807
|
coeff = 1
|
809
808
|
when :half_even, :half_down #, :up # :up # :down, :half_down, :up05, :floor
|
809
|
+
# next largest: 0.0...050...1 (+precision-1 additional digits here)
|
810
810
|
exp = 1-2*precision
|
811
811
|
coeff = 1 + Decimal.int_radix_power(precision)/2
|
812
812
|
when :half_up
|
813
|
+
# next largest: 0.0...05 (precision digits shown to the right of the decimal point)
|
813
814
|
exp = 1-2*precision
|
814
815
|
coeff = Decimal.int_radix_power(precision)/2
|
815
816
|
when :up, :ceiling, :up05
|
817
|
+
# smallest epsilon
|
816
818
|
return minimum_nonzero(sign)
|
817
819
|
end
|
818
820
|
return Decimal(sign, coeff, exp)
|
@@ -1004,16 +1006,20 @@ class Decimal
|
|
1004
1006
|
local_context(*args, &blk)
|
1005
1007
|
elsif args.empty?
|
1006
1008
|
# return the current context
|
1007
|
-
|
1009
|
+
self._context = DefaultContext.dup if _context.nil?
|
1010
|
+
_context
|
1008
1011
|
else
|
1009
1012
|
# change the current context
|
1013
|
+
# TODO: consider doing _context = ... here
|
1014
|
+
# so we would have Decimal.context = c that assigns a duplicate of c
|
1015
|
+
# and Decimal.context c to set alias c
|
1010
1016
|
Decimal.context = define_context(*args)
|
1011
1017
|
end
|
1012
1018
|
end
|
1013
1019
|
|
1014
1020
|
# Change the current context (thread-local).
|
1015
1021
|
def Decimal.context=(c)
|
1016
|
-
|
1022
|
+
self._context = c.dup
|
1017
1023
|
end
|
1018
1024
|
|
1019
1025
|
# Defines a scope with a local context. A context can be passed which will be
|
@@ -1021,9 +1027,9 @@ class Decimal
|
|
1021
1027
|
# options to apply to the local scope.
|
1022
1028
|
# Changes done to the current context are reversed when the scope is exited.
|
1023
1029
|
def Decimal.local_context(*args)
|
1024
|
-
keep = context
|
1025
|
-
Decimal.context = define_context(*args)
|
1026
|
-
result = yield
|
1030
|
+
keep = Decimal.context # use this so _context is initialized if necessary
|
1031
|
+
Decimal.context = define_context(*args) # this dups the assigned context
|
1032
|
+
result = yield _context
|
1027
1033
|
# TODO: consider the convenience of copying the flags from Decimal.context to keep
|
1028
1034
|
# This way a local context does not affect the settings of the previous context,
|
1029
1035
|
# but flags are transferred.
|
@@ -1031,10 +1037,21 @@ class Decimal
|
|
1031
1037
|
# keep.flags = Decimal.context.flags
|
1032
1038
|
# Another alternative to consider: logically or the flags:
|
1033
1039
|
# keep.flags ||= Decimal.context.flags # (this requires implementing || in Flags)
|
1034
|
-
|
1040
|
+
self._context = keep
|
1035
1041
|
result
|
1036
1042
|
end
|
1037
1043
|
|
1044
|
+
class <<self
|
1045
|
+
# This is the thread-local context storage low level interface
|
1046
|
+
protected
|
1047
|
+
def _context #:nodoc:
|
1048
|
+
Thread.current['Decimal.context']
|
1049
|
+
end
|
1050
|
+
def _context=(c) #:nodoc:
|
1051
|
+
Thread.current['Decimal.context'] = c
|
1052
|
+
end
|
1053
|
+
end
|
1054
|
+
|
1038
1055
|
# A decimal number with value zero and the specified sign
|
1039
1056
|
def Decimal.zero(sign=+1)
|
1040
1057
|
Decimal.new([sign, 0, 0])
|
@@ -1301,7 +1318,7 @@ class Decimal
|
|
1301
1318
|
|
1302
1319
|
# Returns whether the number is normal
|
1303
1320
|
def normal?(context=nil)
|
1304
|
-
return
|
1321
|
+
return false if special? || zero?
|
1305
1322
|
context = Decimal.define_context(context)
|
1306
1323
|
(context.emin <= self.adjusted_exponent) && (self.adjusted_exponent <= context.emax)
|
1307
1324
|
end
|
@@ -1334,6 +1351,8 @@ class Decimal
|
|
1334
1351
|
case other
|
1335
1352
|
when *Decimal.context.coercible_types_or_decimal
|
1336
1353
|
[Decimal(other),self]
|
1354
|
+
when Float
|
1355
|
+
[other, self.to_f]
|
1337
1356
|
else
|
1338
1357
|
super
|
1339
1358
|
end
|
@@ -2229,7 +2248,7 @@ class Decimal
|
|
2229
2248
|
end
|
2230
2249
|
end
|
2231
2250
|
else
|
2232
|
-
if defined? other.coerce
|
2251
|
+
if !self.nan? && defined? other.coerce
|
2233
2252
|
x, y = other.coerce(self)
|
2234
2253
|
x <=> y
|
2235
2254
|
else
|
@@ -2535,7 +2554,12 @@ class Decimal
|
|
2535
2554
|
prec = adjusted_exponent + 1
|
2536
2555
|
as_int = true
|
2537
2556
|
end
|
2538
|
-
|
2557
|
+
dg = number_of_digits-prec
|
2558
|
+
changed = _round(r, dg)
|
2559
|
+
coeff = Decimal.int_div_radix_power(@coeff, dg)
|
2560
|
+
exp = @exp + dg
|
2561
|
+
coeff += 1 if changed==1
|
2562
|
+
result = Decimal(@sign, coeff, exp)
|
2539
2563
|
return as_int ? result.to_i : result
|
2540
2564
|
end
|
2541
2565
|
|
@@ -3530,9 +3554,10 @@ class Decimal
|
|
3530
3554
|
if ROUND_ARITHMETIC
|
3531
3555
|
(@coeff % Decimal.int_radix_power(i))==0 ? 0 : -1
|
3532
3556
|
else
|
3557
|
+
return 0 if i==0
|
3533
3558
|
d = @coeff.to_s
|
3534
|
-
|
3535
|
-
|
3559
|
+
tail = d[-i..-1]
|
3560
|
+
(tail.nil? || tail.match(/\A0*\Z/)) ? 0 : -1
|
3536
3561
|
end
|
3537
3562
|
end
|
3538
3563
|
|
@@ -3551,9 +3576,19 @@ class Decimal
|
|
3551
3576
|
_round_half_up(i)
|
3552
3577
|
end
|
3553
3578
|
else
|
3579
|
+
return 0 if i==0
|
3554
3580
|
d = @coeff.to_s
|
3555
3581
|
p = d.size - i
|
3556
|
-
d[p
|
3582
|
+
rdig = d[p,1]
|
3583
|
+
if '6789'.include?(rdig)
|
3584
|
+
1
|
3585
|
+
elsif '1234'.include?(rdig)
|
3586
|
+
-1
|
3587
|
+
elsif rdig=='5'
|
3588
|
+
d[p+1..-1].match(/^0*$/) ? -1 : +1
|
3589
|
+
else # rdig=='0'
|
3590
|
+
d[p..-1].match(/^0*$/) ? 0 : -1
|
3591
|
+
end
|
3557
3592
|
end
|
3558
3593
|
|
3559
3594
|
end
|
@@ -3568,6 +3603,7 @@ class Decimal
|
|
3568
3603
|
(@coeff % m)==0 ? 0 : -1
|
3569
3604
|
end
|
3570
3605
|
else
|
3606
|
+
return 0 if i==0
|
3571
3607
|
d = @coeff.to_s
|
3572
3608
|
p = d.size - i
|
3573
3609
|
if '56789'.include?(d[p,1])
|
@@ -3589,6 +3625,7 @@ class Decimal
|
|
3589
3625
|
_round_half_up(i)
|
3590
3626
|
end
|
3591
3627
|
else
|
3628
|
+
return 0 if i==0
|
3592
3629
|
d = @coeff.to_s
|
3593
3630
|
p = d.size - i
|
3594
3631
|
|
@@ -3679,7 +3716,7 @@ class Decimal
|
|
3679
3716
|
end
|
3680
3717
|
# adj == -1, 0.1 <= self < 1
|
3681
3718
|
return e + (10**-e - c).to_s.length - 1
|
3682
|
-
|
3719
|
+
end
|
3683
3720
|
|
3684
3721
|
module AuxiliarFunctions #:nodoc:
|
3685
3722
|
|
data/lib/decimal/version.rb
CHANGED
@@ -93,7 +93,7 @@ class TestDefineConversions < Test::Unit::TestCase
|
|
93
93
|
assert_equal Decimal('7.1'), Decimal('7')+0.1
|
94
94
|
end
|
95
95
|
assert_equal Decimal('11'), Decimal(11.0)
|
96
|
-
|
96
|
+
assert_instance_of Decimal, Decimal(11.0)
|
97
97
|
|
98
98
|
Decimal.context.define_conversion_from(Float) do |x, context|
|
99
99
|
Decimal.context(context, :exact=>true) do
|
@@ -109,4 +109,33 @@ class TestDefineConversions < Test::Unit::TestCase
|
|
109
109
|
|
110
110
|
end
|
111
111
|
|
112
|
+
def test_conversion_types
|
113
|
+
|
114
|
+
assert_instance_of Decimal, Decimal(1)+3
|
115
|
+
assert_instance_of Decimal, 3+Decimal(1)
|
116
|
+
assert_instance_of Decimal, Rational(1,5)+Decimal(1)
|
117
|
+
assert_instance_of Decimal, Decimal(1)+Rational(1,5)
|
118
|
+
assert_instance_of Float, Decimal(1)+3.0
|
119
|
+
assert_instance_of Float, 3.0+Decimal(1)
|
120
|
+
|
121
|
+
Decimal.context.define_conversion_from(Float) do |x, context|
|
122
|
+
s,e = Math.frexp(x)
|
123
|
+
significand = Math.ldexp(s, Float::MANT_DIG).to_i
|
124
|
+
exponent = e - Float::MANT_DIG
|
125
|
+
# the number is (as a Rational) significand * exponent**Float::RADIX
|
126
|
+
Decimal(significand*(Float::RADIX**exponent ))
|
127
|
+
end
|
128
|
+
|
129
|
+
assert_instance_of Decimal, Decimal(1)+3.0
|
130
|
+
assert_instance_of Decimal, 3.0+Decimal(1)
|
131
|
+
|
132
|
+
Decimal.context.define_conversion_from(BigDecimal) do |x, context|
|
133
|
+
Decimal(x.to_s) # or use x.split etc.
|
134
|
+
end
|
135
|
+
|
136
|
+
assert_instance_of Decimal, Decimal(1)+BigDecimal('3')
|
137
|
+
assert_instance_of Decimal, BigDecimal('3')+Decimal(1)
|
138
|
+
|
139
|
+
end
|
140
|
+
|
112
141
|
end
|
data/test/test_round.rb
CHANGED
@@ -43,6 +43,11 @@ class TestRound < Test::Unit::TestCase
|
|
43
43
|
assert_equal 100, Decimal('100.99999').truncate
|
44
44
|
assert_equal(-100, Decimal('-100.99999').truncate)
|
45
45
|
|
46
|
+
assert_equal(10, Decimal('9.99999').round(:rounding=>:half_up))
|
47
|
+
assert_equal(1, Decimal('0.999999').round(:rounding=>:half_up))
|
48
|
+
assert_equal(0, Decimal('0.0999999').round(:rounding=>:half_up))
|
49
|
+
assert_equal(1, Decimal('0.0999999').round(:rounding=>:up))
|
50
|
+
|
46
51
|
end
|
47
52
|
|
48
53
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-decimal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Javier Goizueta
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-07-21 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|