ruby-decimal 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|