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 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
- Conversion is also possible to Float:
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
- And with GDAS style operations:
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('1.1').to_integral_value -> 1
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:
@@ -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).reduce(self)
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
- Thread.current['Decimal.context'] ||= DefaultContext.dup
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
- Thread.current['Decimal.context'] = c.dup
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.dup
1025
- Decimal.context = define_context(*args)
1026
- result = yield Decimal.context
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
- Decimal.context = keep
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 true if special? || zero?
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
- result = plus(:rounding=>r, :precision=>prec)
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
- p = d.size - i
3535
- d[p..-1].match(/\A0+\Z/) ? 0 : -1
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..-1].match(/^5d*$/) ? -1 : _round_half_up(i)
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
- end
3719
+ end
3683
3720
 
3684
3721
  module AuxiliarFunctions #:nodoc:
3685
3722
 
@@ -2,7 +2,7 @@ module DecimalSupport
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 2
5
- TINY = 1
5
+ TINY = 2
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -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
- assert Decimal(11.0).is_a?(Decimal)
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.1
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-06-23 00:00:00 +02:00
12
+ date: 2009-07-21 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency