ruby-decimal 0.1.0

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.
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + '/helper.rb'
2
+
3
+ class TestCoercion < Test::Unit::TestCase
4
+
5
+
6
+ def setup
7
+ initialize_context
8
+ end
9
+
10
+ def test_coerce
11
+ assert_equal Decimal('7.1'), Decimal('0.1') + 7
12
+ assert_equal Decimal('7.1'), 7 + Decimal('0.1')
13
+ assert_equal Decimal('14'), Decimal(7) * 2
14
+ assert_equal Decimal('14'), 2 * Decimal(7)
15
+
16
+ assert_equal Decimal('7.1'), Decimal(7) + Rational(1,10)
17
+ assert_equal Decimal('7.1'), Rational(1,10) + Decimal(7)
18
+ assert_equal Decimal('1.4'), Decimal(7) * Rational(2,10)
19
+ assert_equal Decimal('1.4'), Rational(2,10) * Decimal(7)
20
+ end
21
+
22
+ end
@@ -0,0 +1,53 @@
1
+ require File.dirname(__FILE__) + '/helper.rb'
2
+
3
+ class TestComparisons < Test::Unit::TestCase
4
+
5
+
6
+ def setup
7
+ initialize_context
8
+ end
9
+
10
+ def test_hash
11
+ assert_equal Decimal('1.1').hash, Decimal('1.1').hash
12
+ assert_equal Decimal('1.1').hash, (Decimal('1.0')+Decimal('0.1')).hash
13
+ assert_equal Decimal('1.1',:precision=>10).hash, Decimal('1.1',:precision=>3).hash
14
+ assert_not_equal Decimal('1.0').hash, Decimal('1.1').hash
15
+ assert_not_equal Decimal('1.0').hash, 1.0.hash
16
+ assert_not_equal Decimal('1.0').hash, 1.hash
17
+
18
+ assert Decimal('1.1').eql?(Decimal('1.1'))
19
+ assert Decimal('1.1').eql?(Decimal('1.0')+Decimal('0.1'))
20
+ assert Decimal('1.1',:precision=>10).eql?(Decimal('1.1',:precision=>3))
21
+ assert !Decimal('1.1').eql?(Decimal('1.0'))
22
+ assert !Decimal('1.0').eql?(1.0)
23
+ assert !Decimal('1.0').eql?(1)
24
+ end
25
+
26
+ def test_equality
27
+ assert Decimal('1.1') == Decimal('1.1')
28
+ assert Decimal('1.1') == (Decimal('1.0')+Decimal('0.1'))
29
+ assert Decimal('1.1',:precision=>10) == Decimal('1.1',:precision=>3)
30
+ assert !(Decimal('1.1') == Decimal('1.0'))
31
+ #assert Decimal('1.1') == 1.1
32
+ #assert Decimal('1.0') == 1.0
33
+ #assert Decimal('1.0') == BigDecimal.new('1.000')
34
+ assert Decimal('1.0') == 1
35
+ assert Decimal('0.1') == Rational(1)/Rational(10)
36
+
37
+ assert !(Decimal.nan == Decimal.nan)
38
+ assert !(Decimal.nan == Decimal('1'))
39
+ assert !(Decimal.nan == Decimal('0'))
40
+ assert !(Decimal.nan == Decimal.infinity)
41
+ #assert !(Decimal.nan == (0.0/0.0))
42
+
43
+ assert !(Decimal.infinity(+1) == Decimal.infinity(-1))
44
+ assert !(Decimal.infinity(+1) == Decimal('0'))
45
+ assert Decimal.infinity(+1) == Decimal.infinity
46
+ assert Decimal.infinity(+1) == Decimal('1')/Decimal('0')
47
+ assert Decimal.infinity(-1) == Decimal('-1')/Decimal('0')
48
+
49
+ # TODO: test <=> <= etc.
50
+ end
51
+
52
+
53
+ end
@@ -0,0 +1,207 @@
1
+ require File.dirname(__FILE__) + '/helper.rb'
2
+
3
+ ROUNDINGS = {
4
+ 'ceiling' => :ceiling,
5
+ 'down' => :down,
6
+ 'floor' => :floor,
7
+ 'half_down' => :half_down,
8
+ 'half_even' => :half_even,
9
+ 'half_up' => :half_up,
10
+ 'up' => :up,
11
+ '05up' => :up05
12
+ }
13
+ FUNCTIONS = {
14
+ 'add'=>'add',
15
+ 'divide'=>'divide',
16
+ 'multiply'=>'multiply',
17
+ 'subtract'=>'subtract',
18
+ 'compare'=>'compare',
19
+ 'copyabs'=>'copy_abs',
20
+ 'copynegate'=>'copy_negate',
21
+ 'copysign'=>'copy_sign',
22
+ 'divideint'=>'divide_int',
23
+ 'logb'=>'logb',
24
+ 'minus'=>'minus',
25
+ 'plus'=>'plus',
26
+ 'reduce'=>'reduce',
27
+ 'remainder'=>'remainder',
28
+ 'remaindernear'=>'remainder_near',
29
+ 'scaleb'=>'scaleb',
30
+ 'rescale'=>'rescale',
31
+ 'quantize'=>'quantize',
32
+ 'samequantum'=>'same_quantum?',
33
+ 'tointegral'=>'to_integral_value',
34
+ 'tointegralx'=>'to_integral_exact',
35
+ 'fma'=>'fma',
36
+ 'squareroot'=>'sqrt',
37
+ 'abs'=>'abs',
38
+ 'nextminus'=>'next_minus',
39
+ 'nextplus'=>'next_plus',
40
+ 'nexttoward'=>'next_toward',
41
+ 'tosci'=>'to_sci_string',
42
+ 'toeng'=>'to_eng_string',
43
+ 'class'=>'number_class'
44
+ }
45
+ # Functions not yet implemented
46
+ PENDING = %w{
47
+ exp
48
+ power
49
+ ln
50
+ log10
51
+
52
+ rotate
53
+ shift
54
+ trim
55
+
56
+ and
57
+ or
58
+ xor
59
+ invert
60
+
61
+ max
62
+ min
63
+ maxmag
64
+ minmag
65
+ comparetotal
66
+ comparetotmag
67
+ }
68
+ IGNORED = PENDING + %w{
69
+ copy
70
+ apply
71
+ }
72
+
73
+ FLAG_NAMES = {
74
+ 'inexact'=>:Inexact,
75
+ 'rounded'=>:Rounded,
76
+ 'clamped'=>:Clamped,
77
+ 'subnormal'=>:Subnormal,
78
+ 'invalid_operation'=>:InvalidOperation,
79
+ 'underflow'=>:Underflow,
80
+ 'overflow'=>:Overflow,
81
+ 'division_by_zero'=>:DivisionByZero,
82
+ 'division_undefined'=>:InvalidOperation,
83
+ 'division_impossible'=>:DivisionImpossible,
84
+ 'conversion_syntax'=>:ConversionSyntax
85
+ }
86
+
87
+
88
+
89
+ def unquote(txt)
90
+ if txt[0,1]=="'" && txt[-1,1]=="'"
91
+ txt = txt[1...-1].gsub("''","'")
92
+ end
93
+ if txt[0,1]=='"' && txt[-1,1]=='"'
94
+ txt = txt[1...-1].gsub('""','"')
95
+ end
96
+ #txt = 'NaN' if txt=='#' || txt=='?'
97
+ txt = 'sNaN' if txt=='#'
98
+ txt = 'NaN' if txt=='?'
99
+ txt
100
+ end
101
+
102
+ class TestBasic < Test::Unit::TestCase
103
+
104
+ def test_dec
105
+ missing = []
106
+ dir = File.join(File.dirname(__FILE__), 'dectest')
107
+ dir = nil unless File.exists?(dir)
108
+ if dir
109
+ Dir[File.join(dir, '*.decTest')].each do |fn|
110
+
111
+ name = File.basename(fn,'.decTest').downcase
112
+ next if %w{ds dd dq}.include?(name[0,2]) ||
113
+ %w{decsingle decdouble decquad testall}.include?(name)
114
+
115
+ initialize_context
116
+
117
+
118
+ File.open(fn,'r') do |file|
119
+ file.each_line do |line|
120
+ next if line[0,2]=='--' || line.strip.empty?
121
+
122
+ if line.include?(' -> ')
123
+ # test
124
+ # to do :remove inline comments --... on the right of ->
125
+ sides = line.split('->')
126
+ # now split by whitespace but avoid breaking quoted strings (and take care or repeated quotes!)
127
+ lhs = sides.first.strip.scan(/"(?:[^"]|"")*"|'(?:[^']|'')*'|\S+/)
128
+ id = lhs.first
129
+ funct = lhs[1].downcase
130
+ valstemp = lhs[2..-1]
131
+ rhs = sides.last.strip.split
132
+ ans = rhs.first
133
+ flags = rhs[1..-1].map{|f| Decimal.class_eval(FLAG_NAMES[f.downcase].to_s)}.compact
134
+
135
+ next unless valstemp.grep(/#/).empty?
136
+
137
+ $test_id = id
138
+ funct = FUNCTIONS[original_funct=funct]
139
+ if funct
140
+ # do test
141
+ msg = "Test #{id}: #{funct}(#{valstemp.join(',')}) = #{ans}"
142
+ #File.open('dectests.txt','a'){|f| f.puts msg}
143
+ expected = result = result_flags = nil
144
+ Decimal.local_context do |context|
145
+ context.flags.clear!
146
+ exact_input = !['apply','to_sci_string', 'to_eng_string'].include?(funct)
147
+ if exact_input
148
+ p = context.precision
149
+ context.exact = true
150
+ end
151
+ valstemp.map!{|v| Decimal(unquote(v))}
152
+ context.precision = p if exact_input
153
+ result = context.send(funct, *valstemp)
154
+ result_flags = context.flags.dup
155
+ expected = unquote(ans)
156
+ context.exact = true
157
+ expected = Decimal(expected) unless result.is_a?(String)
158
+ end
159
+ result = 1 if result==true
160
+ result = 0 if result==false
161
+ expected_flags = Decimal::Flags(*flags)
162
+ if ans!='?'
163
+ assert_equal expected.to_s, result.to_s, msg
164
+ end
165
+ assert_equal expected_flags, result_flags, msg
166
+
167
+ else
168
+ missing << original_funct unless IGNORED.include?(original_funct) || missing.include?(original_funct)
169
+ end
170
+
171
+ elsif line.include?(':')
172
+ # directive
173
+ funct,value = line.split(':').map{|x| x.strip.downcase}
174
+ case funct
175
+ when 'rounding'
176
+ value = ROUNDINGS[value]
177
+ else
178
+ value = value.to_i
179
+ end
180
+ if value.nil?
181
+ #raise "error"
182
+ # to do: skip untill next valid value of same funct
183
+ else
184
+ case funct
185
+ when 'rounding','precision'
186
+ Decimal.context.send "#{funct}=", value
187
+ when 'maxexponent'
188
+ Decimal.context.emax = value
189
+ when 'minexponent'
190
+ Decimal.context.emin = value
191
+ when 'clamp'
192
+ Decimal.context.clamp = (value==0 ? false : true)
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ # assert_empty missing
202
+ # In Ruby 1.8 there's no assert_empty
203
+ assert missing.empty?, "#{missing.inspect} is not empty"
204
+
205
+ end
206
+
207
+ end
@@ -0,0 +1,100 @@
1
+ require File.dirname(__FILE__) + '/helper.rb'
2
+
3
+ class TestDefineConversions < Test::Unit::TestCase
4
+
5
+
6
+ def setup
7
+ initialize_context
8
+ end
9
+
10
+ def test_convert_to
11
+ d = Decimal('1.1')
12
+ x = d.convert_to(Rational)
13
+ assert x.is_a?(Rational)
14
+ assert_equal d.to_r, x
15
+
16
+ d = Decimal('11')
17
+ x = d.convert_to(Integer)
18
+ assert x.is_a?(Integer)
19
+ assert_equal d.to_i, x
20
+
21
+ d = Decimal('11')
22
+ x = d.convert_to(Float)
23
+ assert x.is_a?(Float)
24
+ assert_equal d.to_f, x
25
+ end
26
+
27
+
28
+ def test_big_decimal_conversions
29
+
30
+ Decimal.local_context do
31
+
32
+ Decimal.context.define_conversion_from(BigDecimal) do |x, context|
33
+ Decimal(x.to_s) # or use x.split etc.
34
+ end
35
+ assert Decimal('0') == BigDecimal.new('0')
36
+ assert_equal BigDecimal.new('0'), Decimal('0')
37
+ assert_equal BigDecimal.new('1.2345'), Decimal('1.2345')
38
+ assert_equal BigDecimal.new('-1.2345'), Decimal('-1.2345')
39
+ assert_equal BigDecimal.new('1.2345'), Decimal('0.0012345000E3')
40
+ assert_equal Decimal('7.1'), BigDecimal.new('7')+Decimal('0.1')
41
+ assert_equal Decimal('7.1'), Decimal('7')+BigDecimal.new('0.1')
42
+ assert_equal Decimal('1.1'), Decimal(BigDecimal.new('1.1'))
43
+ assert Decimal(BigDecimal.new('1.1')).is_a?(Decimal)
44
+
45
+ Decimal.context.define_conversion_to(BigDecimal) do |x|
46
+ BigDecimal.new(x.to_s) # TODO: use x.split and handle special values
47
+ end
48
+
49
+ ['0.1', '-0.1', '0.0', '1234567.1234567', '-1234567.1234567', '1.234E7', '1.234E-7'].each do |n|
50
+ f = BigDecimal.new(n)
51
+ d = Decimal(n)
52
+ c = d.convert_to(BigDecimal)
53
+ assert c.is_a?(BigDecimal)
54
+ assert_equal f, c
55
+ end
56
+ end
57
+
58
+ assert_raise(TypeError) { Decimal('0') == BigDecimal.new('0') }
59
+ assert_not_equal BigDecimal.new('0'), Decimal('0')
60
+ assert_not_equal BigDecimal.new('1.2345'), Decimal('1.2345')
61
+ assert_not_equal BigDecimal.new('-1.2345'), Decimal('-1.2345')
62
+ assert_not_equal BigDecimal.new('1.2345'), Decimal('0.0012345000E3')
63
+ assert_raise(TypeError) { BigDecimal.new('7')+Decimal('0.1') }
64
+ assert_raise(TypeError) { Decimal('7')+BigDecimal.new('0.1') }
65
+ assert_raise(TypeError) { Decimal(BigDecimal.new('1.1')) }
66
+
67
+ ['0.1', '-0.1', '0.0', '1234567.1234567', '-1234567.1234567', '1.234E7', '1.234E-7'].each do |n|
68
+ assert_raise(TypeError) { Decimal(n).convert_to(BigDecimal) }
69
+ end
70
+
71
+ end
72
+
73
+ def test_float_conversions
74
+
75
+ # Exact Float to Decimal conversion limited to context precision
76
+ # => Decimal('0.1') != Decimal(0.1) unless precision is low enough
77
+ Decimal.context.define_conversion_from(Float) do |x, context|
78
+ s,e = Math.frexp(x)
79
+ significand = Math.ldexp(s, Float::MANT_DIG).to_i
80
+ exponent = e - Float::MANT_DIG
81
+ # the number is (as a Rational) significand * exponent**Float::RADIX
82
+ Decimal(significand*(Float::RADIX**exponent ))
83
+ end
84
+
85
+ assert_equal 0.0, Decimal('0')
86
+ assert_equal Decimal('0'), 0.0
87
+ assert_equal 1234.5, Decimal('1234.5')
88
+ assert_equal Decimal('1234.5'), 1234.5
89
+ assert_equal(-1234.5, Decimal('-1234.5'))
90
+ assert_equal 1234.5, Decimal('0.0012345000E6')
91
+ assert_equal Decimal('7.1'), 7.0+Decimal('0.1')
92
+ Decimal.local_context(:precision=>12) do
93
+ assert_equal Decimal('7.1'), Decimal('7')+0.1
94
+ end
95
+ assert_equal Decimal('11'), Decimal(11.0)
96
+ assert Decimal(11.0).is_a?(Decimal)
97
+
98
+ end
99
+
100
+ end
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/helper.rb'
2
+
3
+
4
+ class TestExact < Test::Unit::TestCase
5
+
6
+
7
+ def setup
8
+ initialize_context
9
+ end
10
+
11
+
12
+ def test_exact
13
+
14
+ Decimal.context.exact = true
15
+
16
+ assert_equal Decimal("9"*100+"E-50"), Decimal('1E50')-Decimal('1E-50')
17
+ assert_equal Decimal(2),Decimal(6)/Decimal(3)
18
+ assert_equal Decimal('1.5'),Decimal(6)/Decimal(4)
19
+ assert_equal Decimal('15241578780673678546105778281054720515622620750190521'), Decimal('123456789123456789123456789')*Decimal('123456789123456789123456789')
20
+ assert_nothing_raised(Decimal::Inexact){ Decimal(6)/Decimal(4) }
21
+ assert_raise(Decimal::Inexact){ Decimal(1)/Decimal(3) }
22
+ # assert_raise(Decimal::Inexact){ Decimal(2).sqrt }
23
+
24
+ assert_equal Decimal(2), Decimal('4').sqrt
25
+ assert_equal Decimal(4), Decimal('16').sqrt
26
+ assert_raise(Decimal::Inexact){ Decimal(2).sqrt }
27
+
28
+
29
+ end
30
+
31
+
32
+ end
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + '/helper.rb'
2
+
3
+
4
+ class TestFlags < Test::Unit::TestCase
5
+
6
+ def test_flags
7
+ f = DecimalSupport::Flags(:flag_one, :flag_three)
8
+ assert_equal "[:flag_one, :flag_three]", f.to_a.sort_by{|flg| flg.to_s}.inspect
9
+ f.values = DecimalSupport::FlagValues(:flag_one, :flag_two, :flag_three)
10
+ assert_equal "DecimalSupport::Flags[flag_one, flag_three] (0x5)", f.inspect
11
+ f[:flag_two] = true
12
+ assert_equal "DecimalSupport::Flags[flag_one, flag_two, flag_three] (0x7)", f.inspect
13
+ f[:flag_one] = false
14
+ assert_equal "DecimalSupport::Flags[flag_two, flag_three] (0x6)", f.inspect
15
+ f.clear!
16
+ assert_equal "DecimalSupport::Flags[] (0x0)", f.inspect
17
+ f << [:flag_one,:flag_two]
18
+ assert_equal "DecimalSupport::Flags[flag_one, flag_two] (0x3)", f.inspect
19
+ g = DecimalSupport::Flags(f.values)
20
+ g.bits = f.bits
21
+ assert_equal "DecimalSupport::Flags[flag_one, flag_two] (0x3)", g.inspect
22
+ assert g==f
23
+ g.set!
24
+ assert_equal "DecimalSupport::Flags[flag_one, flag_two, flag_three] (0x7)", g.inspect
25
+ assert g!=f
26
+
27
+ assert DecimalSupport::Flags(:flag_one, :flag_three)==DecimalSupport::Flags(:flag_three, :flag_one)
28
+ assert DecimalSupport::Flags(:flag_one, :flag_three)!=DecimalSupport::Flags(:flag_one)
29
+
30
+
31
+
32
+ end
33
+
34
+ end