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.
- data/History.txt +3 -0
- data/License.txt +20 -0
- data/Manifest.txt +24 -0
- data/README.txt +306 -0
- data/Rakefile +35 -0
- data/lib/decimal.rb +4 -0
- data/lib/decimal/decimal.rb +2750 -0
- data/lib/decimal/support.rb +337 -0
- data/lib/decimal/version.rb +9 -0
- data/setup.rb +1585 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +192 -0
- data/tasks/git.rake +40 -0
- data/tasks/manifest.rake +48 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +39 -0
- data/tasks/rdoc.rake +50 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +279 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/test/all_tests.rb +11 -0
- data/test/helper.rb +7 -0
- data/test/test_basic.rb +398 -0
- data/test/test_coercion.rb +22 -0
- data/test/test_comparisons.rb +53 -0
- data/test/test_dectest.rb +207 -0
- data/test/test_define_conversions.rb +100 -0
- data/test/test_exact.rb +32 -0
- data/test/test_flags.rb +34 -0
- data/test/test_multithreading.rb +32 -0
- data/test/test_round.rb +49 -0
- data/test/test_to_int.rb +104 -0
- data/test/test_to_rf.rb +36 -0
- metadata +120 -0
data/History.txt
ADDED
data/License.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 Javier Goizueta
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
History.txt
|
2
|
+
License.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
lib/decimal.rb
|
7
|
+
lib/decimal/version.rb
|
8
|
+
lib/decimal/decimal.rb
|
9
|
+
lib/decimal/support.rb
|
10
|
+
setup.rb
|
11
|
+
test/all_tests.rb
|
12
|
+
test/helper.rb
|
13
|
+
test/test_basic.rb
|
14
|
+
test/test_coercion.rb
|
15
|
+
test/test_comparisons.rb
|
16
|
+
test/test_define_conversions.rb
|
17
|
+
test/test_flags.rb
|
18
|
+
test/test_multithreading.rb
|
19
|
+
test/test_dectest.rb
|
20
|
+
test/test_round.rb
|
21
|
+
test/test_exact.rb
|
22
|
+
test/test_to_int.rb
|
23
|
+
test/test_to_rf.rb
|
24
|
+
|
data/README.txt
ADDED
@@ -0,0 +1,306 @@
|
|
1
|
+
= Introduction
|
2
|
+
|
3
|
+
Decimal is a standards-compliant arbitrary precision decimal floating-point type for Ruby.
|
4
|
+
It is based on the Python Decimal class.
|
5
|
+
|
6
|
+
The current implementation is written completely in Ruby, so it is rather slow.
|
7
|
+
The intentention is to experiment with this pure-ruby implementation to
|
8
|
+
define a nice feature-set and API for Decimal and have a good test suite for its
|
9
|
+
specification. Then an efficient implementation could be written, for example
|
10
|
+
by using a C extension wrapper around the decNumber library.
|
11
|
+
|
12
|
+
== Standars compliance.
|
13
|
+
|
14
|
+
Decimal pretends to be conformant to the General Decimal Arithmetic Specification
|
15
|
+
and the revised IEEE 754 standard (IEEE 754-2008).
|
16
|
+
|
17
|
+
= Examples of use
|
18
|
+
|
19
|
+
To install the library use gem from the command line: (you may not need sudo)
|
20
|
+
sudo gem install ruby-decimal
|
21
|
+
|
22
|
+
Then require the library in your code:
|
23
|
+
require 'decimal'
|
24
|
+
|
25
|
+
Now we can use the Decimal class simply like this:
|
26
|
+
|
27
|
+
puts Decimal(1)/Decimal(3) -> 0.3333333333333333333333333333
|
28
|
+
|
29
|
+
Decimal() is a constructor that can be used instead of Decimal.new()
|
30
|
+
|
31
|
+
== Contexts
|
32
|
+
|
33
|
+
Contexts are environments for arithmetic operations. They govern precision, set rules
|
34
|
+
for rounding, determine which signals are treated as exceptions, and limit the range
|
35
|
+
for exponents.
|
36
|
+
|
37
|
+
Each thread has an active context that can be accessed like this:
|
38
|
+
|
39
|
+
puts Decimal.context.precision -> 28
|
40
|
+
|
41
|
+
The active context can be globally for the current thread:
|
42
|
+
|
43
|
+
Decimal.context.precision = 2
|
44
|
+
puts Decimal.context.precision -> 2
|
45
|
+
puts Decimal(1)/Decimal(3) -> 0.33
|
46
|
+
Decimal.context.precision += 7
|
47
|
+
puts Decimal.context.precision -> 9
|
48
|
+
puts Decimal(1)/Decimal(3) -> 0.333333333
|
49
|
+
|
50
|
+
Or it can be altered locally inside a block:
|
51
|
+
|
52
|
+
Decimal.context do
|
53
|
+
Decimal.context.precision = 5
|
54
|
+
puts Decimal.context.precision
|
55
|
+
end -> 5
|
56
|
+
puts Decimal.context.precision -> 9
|
57
|
+
|
58
|
+
The block for a local context can be passed the current context as an argument:
|
59
|
+
|
60
|
+
Decimal.context do |local_context|
|
61
|
+
local_context.precision = 5
|
62
|
+
puts Decimal.context.precision
|
63
|
+
end -> 5
|
64
|
+
puts Decimal.context.precision -> 9
|
65
|
+
|
66
|
+
A context object can be used to define the local context:
|
67
|
+
|
68
|
+
my_context = Decimal::Context(:precision=>20)
|
69
|
+
Decimal.context(my_context) do |context|
|
70
|
+
puts context.precision
|
71
|
+
end -> 20
|
72
|
+
|
73
|
+
And individual parameters can be assigned like this:
|
74
|
+
|
75
|
+
puts Decimal.context.precision -> 9
|
76
|
+
puts Decimal.context.rounding -> half_even
|
77
|
+
Decimal.context(:rounding=>:down) do |context|
|
78
|
+
puts context.precision
|
79
|
+
puts context.rounding
|
80
|
+
end
|
81
|
+
|
82
|
+
Contexts created with the Decimal::Context() constructor
|
83
|
+
inherit from Decimal::DefaultContext.
|
84
|
+
Default context attributes can be established by modifying
|
85
|
+
that object:
|
86
|
+
|
87
|
+
Decimal::DefaultContext.precision = 10
|
88
|
+
Decimal.context = Decimal::Context(:rounding=>:half_up)
|
89
|
+
puts Decimal.context.precision -> 10
|
90
|
+
|
91
|
+
Note that a context object assigned to Decimal.context is copied,
|
92
|
+
so it is not altered through Decimal.context:
|
93
|
+
|
94
|
+
puts my_context.precision -> 20
|
95
|
+
Decimal.context = my_context
|
96
|
+
Decimal.context.precision = 2
|
97
|
+
puts my_context.precision -> 20
|
98
|
+
|
99
|
+
So, DefaultContext is not altered when modifying Decimal.context.
|
100
|
+
|
101
|
+
Methods that use a context have an optional parameter to override
|
102
|
+
the active context (Decimal.context) :
|
103
|
+
|
104
|
+
Decimal.context.precision = 3
|
105
|
+
puts Decimal(1).divide(3) -> 0.333
|
106
|
+
puts Decimal(1).divide(3, my_context) -> 0.33333333333333333333
|
107
|
+
|
108
|
+
Individual context parameters can also be overriden:
|
109
|
+
|
110
|
+
puts Decimal(1).divide(3, :precision=>6) -> 0.333333
|
111
|
+
|
112
|
+
There are two additional predefined contexts Decimal::ExtendedContext
|
113
|
+
and Decimal::BasicContext that are not meant to be modified; they
|
114
|
+
can be used to achieve reproducible results. We will use
|
115
|
+
Decimal::ExtendedContext in the following examples:
|
116
|
+
|
117
|
+
Decimal.context = Decimal::ExtendedContext
|
118
|
+
|
119
|
+
==Rounding
|
120
|
+
|
121
|
+
Results are normally rounded using the precision (number of significant digits)
|
122
|
+
and rounding mode defined in the context.
|
123
|
+
|
124
|
+
Decimal.context.precision = 4
|
125
|
+
puts Decimal(1)/Decimal(3) -> 0.3333
|
126
|
+
puts Decimal('1E20')-Decimal('1E-20') -> 1.000E+20
|
127
|
+
Decimal.context.rounding = :half_up
|
128
|
+
puts +Decimal('100.05') -> 100.1
|
129
|
+
Decimal.context.rounding = :half_even
|
130
|
+
puts +Decimal('100.05') -> 100.0
|
131
|
+
|
132
|
+
Note that input values are not rounded, only results; we use
|
133
|
+
the plus operator to force rounding here:
|
134
|
+
|
135
|
+
Decimal.context.precision = 4
|
136
|
+
x = Decimal('123.45678')
|
137
|
+
puts x -> 123.45678
|
138
|
+
puts +x -> 123.5
|
139
|
+
|
140
|
+
Precision can be also set to exact to avoid rounding, by using
|
141
|
+
the exact property or using a 0 precision. In exact mode results
|
142
|
+
are never rounded and results that have an infinite number of
|
143
|
+
digits trigger the Decimal::Inexact exception.
|
144
|
+
|
145
|
+
Decimal.context.exact = true
|
146
|
+
puts Decimal('1E20')-Decimal('1E-20') -> 99999999999999999999.99999999999999999999
|
147
|
+
puts Decimal(16).sqrt -> 4
|
148
|
+
puts Decimal(16)/Decimal(4) -> 4
|
149
|
+
puts Decimal(1)/Decimal(3) -> Exception : Decimal::Inexact
|
150
|
+
|
151
|
+
Decimal.context.precision = 5
|
152
|
+
puts Decimal('1E20')-Decimal('1E-20') -> 1.0000E+20
|
153
|
+
puts Decimal(16).sqrt -> 4
|
154
|
+
puts Decimal(16)/Decimal(4) -> 4
|
155
|
+
puts Decimal(1)/Decimal(3) -> 0.33333
|
156
|
+
|
157
|
+
There are also some methods for explicit rounding that provide
|
158
|
+
an interface compatible with the Ruby interface of Float:
|
159
|
+
|
160
|
+
puts Decimal('101.5').round -> 102
|
161
|
+
puts Decimal('101.5').round(0) -> 102
|
162
|
+
puts Decimal('101.12345').round(2) -> 101.12
|
163
|
+
puts Decimal('101.12345').round(-1) -> 1.0E+2
|
164
|
+
puts Decimal('101.12345').round(:places=>2) -> 101.12
|
165
|
+
puts Decimal('101.12345').round(:precision=>2) -> 1.0E+2
|
166
|
+
puts Decimal('101.5').round(:rounding=>:half_up) -> 102
|
167
|
+
puts Decimal('101.5').ceil -> 102
|
168
|
+
puts Decimal('101.5').floor -> 101
|
169
|
+
puts Decimal('101.5').truncate -> 101
|
170
|
+
|
171
|
+
==Special values
|
172
|
+
TODO
|
173
|
+
|
174
|
+
==Exceptions
|
175
|
+
TODO
|
176
|
+
|
177
|
+
==Numerical conversion
|
178
|
+
|
179
|
+
By default, Decimal is interoperable with Integer and Rational.
|
180
|
+
Conversion happens automatically to operands:
|
181
|
+
|
182
|
+
puts Decimal('0.1') + 1 -> 1.1
|
183
|
+
puts 7 + Decimal('0.2') -> 7.2
|
184
|
+
puts Rational(5,2) + Decimal('3') -> 5.5
|
185
|
+
|
186
|
+
Conversion can also be done explicitely with
|
187
|
+
the Decimal constructor:
|
188
|
+
|
189
|
+
puts Decimal(7) -> 7
|
190
|
+
puts Decimal(Rational(1,10)) -> 0.1
|
191
|
+
|
192
|
+
Converting a Decimal to other numerical types can be done with specific Ruby-style methods.
|
193
|
+
|
194
|
+
puts Decimal('1.1').to_i -> 1
|
195
|
+
puts Decimal('1.1').to_r -> 11/10
|
196
|
+
|
197
|
+
(note the truncated result of to_i)
|
198
|
+
Or with a generic method:
|
199
|
+
puts Decimal('1.1').convert_to(Integer) -> 1
|
200
|
+
puts Decimal('1.1').convert_to(Rational) -> 11/10
|
201
|
+
|
202
|
+
Conversion is also possible to Float:
|
203
|
+
puts Decimal('1.1').to_f -> 1.1
|
204
|
+
puts Decimal('1.1').convert_to(Float) -> 1.1
|
205
|
+
|
206
|
+
And with GDAS style operations:
|
207
|
+
|
208
|
+
puts Decimal('1.1').to_integral_value -> 1
|
209
|
+
|
210
|
+
The conversion system is extensible. For example, we can include BigDecimal into it
|
211
|
+
by defining suitable conversion procedures:
|
212
|
+
|
213
|
+
Decimal.context.define_conversion_from(BigDecimal) do |x, context|
|
214
|
+
Decimal(x.to_s)
|
215
|
+
end
|
216
|
+
Decimal.context.define_conversion_to(BigDecimal) do |x|
|
217
|
+
BigDecimal.new(x.to_s)
|
218
|
+
end
|
219
|
+
|
220
|
+
Now we can mix BigDecimals and Decimals in expressions and convert from Decimal
|
221
|
+
to BigDecimal:
|
222
|
+
|
223
|
+
puts BigDecimal.new('1.1') + Decimal('2.2') -> 3.3
|
224
|
+
puts Decimal('1.1').convert_to(BigDecimal) -> 0.11E1
|
225
|
+
|
226
|
+
Note that the conversions are defined in a Context object and will be available only
|
227
|
+
when that context applies. That way we can define conversions for specific purposes
|
228
|
+
without affecting a program globally.
|
229
|
+
|
230
|
+
As another example consider conversion from Float to Decimal, which is not defined by
|
231
|
+
default because it can be defined in different ways depending on the purpose.
|
232
|
+
|
233
|
+
A Float constant such as 0.1 defines a Float object which has a numerical value close to,
|
234
|
+
but not exactly 1/10. When converting that Float to Decimal we could decide to preserver
|
235
|
+
the exact numerical value of the number or try to find a simple decimal expression within
|
236
|
+
a given tolerance. If we take the first approach we can define this conversion:
|
237
|
+
|
238
|
+
Decimal.context.define_conversion_from(Float) do |x, context|
|
239
|
+
s,e = Math.frexp(x)
|
240
|
+
s = Math.ldexp(s, Float::MANT_DIG).to_i
|
241
|
+
e -= Float::MANT_DIG
|
242
|
+
Decimal(s*(Float::RADIX**e))
|
243
|
+
end
|
244
|
+
|
245
|
+
Note that the conversion we've defined depends on the context precision:
|
246
|
+
|
247
|
+
Decimal.local_context(:precision=>20) { puts Decimal(0.1) } -> 0.10000000000000000555
|
248
|
+
|
249
|
+
Decimal.local_context(:precision=>12) { puts Decimal(0.1) } -> 0.100000000000
|
250
|
+
|
251
|
+
== Available functionality
|
252
|
+
|
253
|
+
Consult the documentation for the classes Decimal and Decimal::Context.
|
254
|
+
|
255
|
+
= Decimal vs BigDecimal
|
256
|
+
|
257
|
+
--
|
258
|
+
EXPAND-
|
259
|
+
++
|
260
|
+
|
261
|
+
Decimal solves some of the difficulties of using BigDecimal.
|
262
|
+
|
263
|
+
One of the major problems with BigDecimal is that it's not easy to control the number of
|
264
|
+
significant digits: while addition, subtraction and multiplication are exact (unless a limit is used),
|
265
|
+
divisions will need to be passed precision explicitly or they will loose an indeterminate number of digits.
|
266
|
+
With Decimal, Context objects are used to specify the exact number of digits to be used for all operations:
|
267
|
+
Decimal.context.precision = 10
|
268
|
+
puts Decimal(1)/Decimal(3)
|
269
|
+
Contexts are thread-safe and can be used for individual operations:
|
270
|
+
puts Decimal(1).divide(Decimal(e), Decimal::Context.new(:precision=>4))
|
271
|
+
Or use locally in a block without affecting other code:
|
272
|
+
Decimal.local_context {
|
273
|
+
Decimal.context.precision = 3
|
274
|
+
puts Decimal(1)/Decimal(3)
|
275
|
+
}
|
276
|
+
puts Decimal.context.precision
|
277
|
+
|
278
|
+
This allows in general to write simpler code; e.g. this is an exponential function, adapted from the
|
279
|
+
'recipes' in Python's Decimal:
|
280
|
+
def exp(x,c=nil)
|
281
|
+
i, lasts, s, fact, num = 0, 0, 1, 1, 1
|
282
|
+
Decimal.local_context(c) do |context|
|
283
|
+
context.precision += 2
|
284
|
+
while s != lasts
|
285
|
+
lasts = s
|
286
|
+
i += 1
|
287
|
+
fact *= i
|
288
|
+
num *= x
|
289
|
+
s += num / fact
|
290
|
+
end
|
291
|
+
end
|
292
|
+
return +s
|
293
|
+
end
|
294
|
+
The final unary + applied to the result forces it to be rounded to the current precision
|
295
|
+
(because we have computed it with two extra digits)
|
296
|
+
The result of this method does not have trailing insignificant digits, as is common with BigDecimal.
|
297
|
+
|
298
|
+
--
|
299
|
+
EXPAND+
|
300
|
+
++
|
301
|
+
|
302
|
+
= Roadmap
|
303
|
+
|
304
|
+
* Complete documentation (README sections on special values & exceptions, etc. and method descriptions.)
|
305
|
+
* Version 0.2.0: Implement GDAS exp(), power(), ln() log10() and also the Ruby-style operator **.
|
306
|
+
* Version 0.3.0:
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Look in the tasks/setup.rb file for the various options that can be
|
2
|
+
# configured in this Rakefile. The .rake files in the tasks directory
|
3
|
+
# are where the options are used.
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bones'
|
7
|
+
Bones.setup
|
8
|
+
rescue LoadError
|
9
|
+
load 'tasks/setup.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
ensure_in_path 'lib'
|
13
|
+
#require 'decimal'
|
14
|
+
require 'decimal/version'
|
15
|
+
|
16
|
+
task :default => 'spec:run'
|
17
|
+
|
18
|
+
|
19
|
+
PROJ.name = 'ruby-decimal'
|
20
|
+
PROJ.description = "Ruby Decimal Type"
|
21
|
+
PROJ.authors = 'Javier Goizueta'
|
22
|
+
PROJ.email = 'javier@goizueta.info'
|
23
|
+
PROJ.version = DecimalSupport::VERSION::STRING
|
24
|
+
PROJ.rubyforge.name = 'ruby-decimal'
|
25
|
+
PROJ.url = "http://#{PROJ.rubyforge.name}.rubyforge.org"
|
26
|
+
PROJ.rdoc.opts = [
|
27
|
+
"--main", "README.txt",
|
28
|
+
'--title', 'Ruby Decimal Documentation',
|
29
|
+
"--opname", "index.html",
|
30
|
+
"--line-numbers",
|
31
|
+
"--inline-source"
|
32
|
+
]
|
33
|
+
#PROJ.test.file = 'test/all_tests.rb'
|
34
|
+
|
35
|
+
# EOF
|
data/lib/decimal.rb
ADDED
@@ -0,0 +1,2750 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'rational'
|
4
|
+
require 'monitor'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
# Decimal arbitrary precision floating point number.
|
8
|
+
# This implementation of Decimal is based on the Decimal module of Python,
|
9
|
+
# written by Eric Price, Facundo Batista, Raymond Hettinger, Aahz and Tim Peters.
|
10
|
+
class Decimal
|
11
|
+
|
12
|
+
extend DecimalSupport # allows use of unqualified FlagValues(), Flags()
|
13
|
+
|
14
|
+
ROUND_HALF_EVEN = :half_even
|
15
|
+
ROUND_HALF_DOWN = :half_down
|
16
|
+
ROUND_HALF_UP = :half_up
|
17
|
+
ROUND_FLOOR = :floor
|
18
|
+
ROUND_CEILING = :ceiling
|
19
|
+
ROUND_DOWN = :down
|
20
|
+
ROUND_UP = :up
|
21
|
+
ROUND_05UP = :up05
|
22
|
+
|
23
|
+
# Numerical conversion base support
|
24
|
+
# base (default) coercible types associated to procedures for numerical conversion
|
25
|
+
@base_coercible_types = {
|
26
|
+
Integer=>lambda{|x, context| x>=0 ? [+1,x,0] : [-1,-x,0]},
|
27
|
+
Rational=>lambda{|x, context|
|
28
|
+
x, y = Decimal.new(x.numerator), Decimal.new(x.denominator)
|
29
|
+
x.divide(y, context)
|
30
|
+
}
|
31
|
+
}
|
32
|
+
@base_conversions = {
|
33
|
+
Integer=>:to_i, Rational=>:to_r, Float=>:to_f
|
34
|
+
}
|
35
|
+
class <<self
|
36
|
+
attr_reader :base_coercible_types
|
37
|
+
attr_reader :base_conversions
|
38
|
+
end
|
39
|
+
|
40
|
+
# Numerical base of Decimal.
|
41
|
+
def self.radix
|
42
|
+
10
|
43
|
+
end
|
44
|
+
|
45
|
+
# Integral power of the base: radix**n for integer n; returns an integer.
|
46
|
+
def self.int_radix_power(n)
|
47
|
+
10**n
|
48
|
+
end
|
49
|
+
|
50
|
+
# Multiply by an integral power of the base: x*(radix**n) for x,n integer;
|
51
|
+
# returns an integer.
|
52
|
+
def self.int_mult_radix_power(x,n)
|
53
|
+
x * (10**n)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Divide by an integral power of the base: x/(radix**n) for x,n integer;
|
57
|
+
# returns an integer.
|
58
|
+
def self.int_div_radix_power(x,n)
|
59
|
+
x / (10**n)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# Base class for errors.
|
64
|
+
class Error < StandardError
|
65
|
+
end
|
66
|
+
|
67
|
+
# Base class for exceptions.
|
68
|
+
#
|
69
|
+
# All exception conditions derive from this class.
|
70
|
+
# The exception classes also define the values returned when trapping is disable for
|
71
|
+
# a particular exception.
|
72
|
+
class Exception < StandardError
|
73
|
+
attr :context
|
74
|
+
def initialize(context=nil)
|
75
|
+
@context = context
|
76
|
+
end
|
77
|
+
|
78
|
+
# Defines the value returned when trapping is inactive
|
79
|
+
# for the condition. The arguments are those passed to
|
80
|
+
# Context#exception after the message.
|
81
|
+
def self.handle(context, *args)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Invalid operation exception.
|
86
|
+
#
|
87
|
+
# The result of the operation is a quiet positive NaN,
|
88
|
+
# except when the cause is a signaling NaN, in which case the result is
|
89
|
+
# also a quiet NaN, but with the original sign, and an optional
|
90
|
+
# diagnostic information.
|
91
|
+
class InvalidOperation < Exception
|
92
|
+
def self.handle(context=nil, *args)
|
93
|
+
if args.size>0
|
94
|
+
sign, coeff, exp = args.first.split
|
95
|
+
Decimal.new([sign, coeff, :nan])._fix_nan(context)
|
96
|
+
else
|
97
|
+
Decimal.nan
|
98
|
+
end
|
99
|
+
end
|
100
|
+
def initialize(context=nil, *args)
|
101
|
+
@value = args.first if args.size>0
|
102
|
+
super
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Division by zero exception.
|
107
|
+
#
|
108
|
+
# The result of the operation is +/-Infinity, where the sign is the product
|
109
|
+
# of the signs of the operands for divide, or 1 for an odd power of -0.
|
110
|
+
class DivisionByZero < Exception
|
111
|
+
def self.handle(context,sign,*args)
|
112
|
+
Decimal.infinity(sign)
|
113
|
+
end
|
114
|
+
def initialize(context=nil, sign=nil, *args)
|
115
|
+
@sign = sign
|
116
|
+
super
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Cannot perform the division adequately exception.
|
121
|
+
#
|
122
|
+
# This occurs and signals invalid-operation if the integer result of a
|
123
|
+
# divide-integer or remainder operation had too many digits (would be
|
124
|
+
# longer than precision).
|
125
|
+
# The result is NaN.
|
126
|
+
class DivisionImpossible < Exception
|
127
|
+
def self.handle(context,*args)
|
128
|
+
Decimal.nan
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Undefined result of division exception.
|
133
|
+
#
|
134
|
+
# This occurs and signals invalid-operation if division by zero was
|
135
|
+
# attempted (during a divide-integer, divide, or remainder operation), and
|
136
|
+
# the dividend is also zero.
|
137
|
+
# The result is NaN.
|
138
|
+
class DivisionUndefined < Exception
|
139
|
+
def self.handle(context,*args)
|
140
|
+
Decimal.nan
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Inexact Exception.
|
145
|
+
#
|
146
|
+
# This occurs and signals inexact whenever the result of an operation is
|
147
|
+
# not exact (that is, it needed to be rounded and any discarded digits
|
148
|
+
# were non-zero), or if an overflow or underflow condition occurs. The
|
149
|
+
# result in all cases is unchanged.
|
150
|
+
class Inexact < Exception
|
151
|
+
end
|
152
|
+
|
153
|
+
# Overflow Exception.
|
154
|
+
#
|
155
|
+
# This occurs and signals overflow if the adjusted exponent of a result
|
156
|
+
# (from a conversion or from an operation that is not an attempt to divide
|
157
|
+
# by zero), after rounding, would be greater than the largest value that
|
158
|
+
# can be handled by the implementation (the value Emax).
|
159
|
+
#
|
160
|
+
# The result depends on the rounding mode:
|
161
|
+
#
|
162
|
+
# For round-half-up and round-half-even (and for round-half-down and
|
163
|
+
# round-up, if implemented), the result of the operation is +/-Infinity,
|
164
|
+
# where the sign is that of the intermediate result. For round-down, the
|
165
|
+
# result is the largest finite number that can be represented in the
|
166
|
+
# current precision, with the sign of the intermediate result. For
|
167
|
+
# round-ceiling, the result is the same as for round-down if the sign of
|
168
|
+
# the intermediate result is 1, or is +Infinity otherwise. For round-floor,
|
169
|
+
# the result is the same as for round-down if the sign of the intermediate
|
170
|
+
# result is 0, or is -Infinity otherwise. In all cases, Inexact and Rounded
|
171
|
+
# will also be raised.
|
172
|
+
class Overflow < Exception
|
173
|
+
def self.handle(context, sign, *args)
|
174
|
+
if [:half_up, :half_even, :half_down, :up].include?(context.rounding)
|
175
|
+
Decimal.infinity(sign)
|
176
|
+
elsif sign==+1
|
177
|
+
if context.rounding == :ceiling
|
178
|
+
Decimal.infinity(sign)
|
179
|
+
else
|
180
|
+
Decimal.new([sign, Decimal.int_radix_power(context.precision) - 1, context.emax - context.precision + 1])
|
181
|
+
end
|
182
|
+
elsif sign==-1
|
183
|
+
if context.rounding == :floor
|
184
|
+
Decimal.infinity(sign)
|
185
|
+
else
|
186
|
+
Decimal.new([sign, Decimal.int_radix_power(context.precision) - 1, context.emax - context.precision + 1])
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
def initialize(context=nil, sign=nil, *args)
|
191
|
+
@sign = sign
|
192
|
+
super
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Numerical Underflow with result rounded to 0 exception.
|
197
|
+
#
|
198
|
+
# This occurs and signals underflow if a result is inexact and the
|
199
|
+
# adjusted exponent of the result would be smaller (more negative) than
|
200
|
+
# the smallest value that can be handled by the implementation (the value
|
201
|
+
# emin). That is, the result is both inexact and subnormal.
|
202
|
+
#
|
203
|
+
# The result after an underflow will be a subnormal number rounded, if
|
204
|
+
# necessary, so that its exponent is not less than Etiny. This may result
|
205
|
+
# in 0 with the sign of the intermediate result and an exponent of etiny.
|
206
|
+
#
|
207
|
+
# In all cases, Inexact, Rounded, and Subnormal will also be raised.
|
208
|
+
class Underflow < Exception
|
209
|
+
end
|
210
|
+
|
211
|
+
# Clamped exception: exponent of a 0 changed to fit bounds.
|
212
|
+
#
|
213
|
+
# This occurs and signals clamped if the exponent of a result has been
|
214
|
+
# altered in order to fit the constraints of a specific concrete
|
215
|
+
# representation. This may occur when the exponent of a zero result would
|
216
|
+
# be outside the bounds of a representation, or when a large normal
|
217
|
+
# number would have an encoded exponent that cannot be represented. In
|
218
|
+
# this latter case, the exponent is reduced to fit and the corresponding
|
219
|
+
# number of zero digits are appended to the coefficient ("fold-down").
|
220
|
+
class Clamped < Exception
|
221
|
+
end
|
222
|
+
|
223
|
+
# Invalid context exception.
|
224
|
+
#
|
225
|
+
# This occurs and signals invalid-operation if an invalid context was
|
226
|
+
# detected during an operation. This can occur if contexts are not checked
|
227
|
+
# on creation and either the precision exceeds the capability of the
|
228
|
+
# underlying concrete representation or an unknown or unsupported rounding
|
229
|
+
# was specified. These aspects of the context need only be checked when
|
230
|
+
# the values are required to be used. The result is NaN.
|
231
|
+
class InvalidContext < Exception
|
232
|
+
def self.handle(context,*args)
|
233
|
+
Decimal.nan
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Number got rounded exception (not necessarily changed during rounding).
|
238
|
+
#
|
239
|
+
# This occurs and signals rounded whenever the result of an operation is
|
240
|
+
# rounded (that is, some zero or non-zero digits were discarded from the
|
241
|
+
# coefficient), or if an overflow or underflow condition occurs. The
|
242
|
+
# result in all cases is unchanged.
|
243
|
+
class Rounded < Exception
|
244
|
+
end
|
245
|
+
|
246
|
+
# Exponent < emin before rounding exception.
|
247
|
+
#
|
248
|
+
# This occurs and signals subnormal whenever the result of a conversion or
|
249
|
+
# operation is subnormal (that is, its adjusted exponent is less than
|
250
|
+
# Emin, before any rounding). The result in all cases is unchanged.
|
251
|
+
class Subnormal < Exception
|
252
|
+
end
|
253
|
+
|
254
|
+
# Conversion syntax error exception (Trying to convert badly formed string.)
|
255
|
+
#
|
256
|
+
# This occurs and signals invalid-operation if an string is being
|
257
|
+
# converted to a number and it does not conform to the numeric string
|
258
|
+
# syntax. The result is NaN.
|
259
|
+
class ConversionSyntax < InvalidOperation
|
260
|
+
def self.handle(context, *args)
|
261
|
+
Decimal.nan
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
EXCEPTIONS = FlagValues(Clamped, InvalidOperation, DivisionByZero, Inexact, Overflow, Underflow,
|
266
|
+
Rounded, Subnormal, DivisionImpossible, ConversionSyntax)
|
267
|
+
|
268
|
+
def self.Flags(*values)
|
269
|
+
DecimalSupport::Flags(EXCEPTIONS,*values)
|
270
|
+
end
|
271
|
+
|
272
|
+
# The context defines the arithmetic context: rounding mode, precision,...
|
273
|
+
# Decimal.context is the current (thread-local) context.
|
274
|
+
class Context
|
275
|
+
|
276
|
+
# If an options hash is passed, the options are
|
277
|
+
# applied to the default context; if a Context is passed as the first
|
278
|
+
# argument, it is used as the base instead of the default context.
|
279
|
+
#
|
280
|
+
# The valid options are:
|
281
|
+
# * :rounding : one of :half_even, :half_down, :half_up, :floor,
|
282
|
+
# :ceiling, :down, :up, :up05
|
283
|
+
# * :precision : number of digits (or 0 for exact precision)
|
284
|
+
# * :exact : true or false (precision is ignored when true)
|
285
|
+
# * :traps : a Flags object with the exceptions to be trapped
|
286
|
+
# * :flags : a Flags object with the raised flags
|
287
|
+
# * :ignored_flags : a Flags object with the exceptions to be ignored
|
288
|
+
# * :emin, :emax : minimum and maximum exponents
|
289
|
+
# * :capitals : (true or false) to use capitals in text representations
|
290
|
+
# * :clamp : (true or false) enables clamping
|
291
|
+
#
|
292
|
+
# See also the context constructor method Decimal.Context().
|
293
|
+
def initialize(*options)
|
294
|
+
|
295
|
+
if options.first.instance_of?(Context)
|
296
|
+
base = options.shift
|
297
|
+
copy_from base
|
298
|
+
else
|
299
|
+
@ignored_flags = Decimal::Flags()
|
300
|
+
@traps = Decimal::Flags()
|
301
|
+
@flags = Decimal::Flags()
|
302
|
+
@coercible_type_handlers = Decimal.base_coercible_types.dup
|
303
|
+
@conversions = Decimal.base_conversions.dup
|
304
|
+
end
|
305
|
+
assign options.first
|
306
|
+
|
307
|
+
end
|
308
|
+
|
309
|
+
attr_accessor :rounding, :emin, :emax, :flags, :traps, :ignored_flags, :capitals, :clamp
|
310
|
+
|
311
|
+
# Ignore all flags if they are raised
|
312
|
+
def ignore_all_flags
|
313
|
+
#@ignored_flags << EXCEPTIONS
|
314
|
+
@ignored_flags.set!
|
315
|
+
end
|
316
|
+
|
317
|
+
# Ignore a specified set of flags if they are raised
|
318
|
+
def ignore_flags(*flags)
|
319
|
+
#@ignored_flags << flags
|
320
|
+
@ignored_flags.set(*flags)
|
321
|
+
end
|
322
|
+
|
323
|
+
# Stop ignoring a set of flags, if they are raised
|
324
|
+
def regard_flags(*flags)
|
325
|
+
@ignored_flags.clear(*flags)
|
326
|
+
end
|
327
|
+
|
328
|
+
# 'tiny' exponet (emin - precision + 1)
|
329
|
+
def etiny
|
330
|
+
emin - precision + 1
|
331
|
+
end
|
332
|
+
|
333
|
+
# maximum exponent (emax - precision + 1)
|
334
|
+
def etop
|
335
|
+
emax - precision + 1
|
336
|
+
end
|
337
|
+
|
338
|
+
# synonym for precision()
|
339
|
+
def digits
|
340
|
+
self.precision
|
341
|
+
end
|
342
|
+
|
343
|
+
# synonym for precision=()
|
344
|
+
def digits=(n)
|
345
|
+
self.precision=n
|
346
|
+
end
|
347
|
+
|
348
|
+
# synonym for precision()
|
349
|
+
def prec
|
350
|
+
self.precision
|
351
|
+
end
|
352
|
+
|
353
|
+
# synonym for precision=()
|
354
|
+
def prec=(n)
|
355
|
+
self.precision = n
|
356
|
+
end
|
357
|
+
|
358
|
+
# is clamping enabled?
|
359
|
+
def clamp?
|
360
|
+
@clamp
|
361
|
+
end
|
362
|
+
|
363
|
+
# Set the number of digits of precision.
|
364
|
+
# If 0 is set the precision turns to be exact.
|
365
|
+
def precision=(n)
|
366
|
+
@precision = n
|
367
|
+
@exact = false unless n==0
|
368
|
+
update_precision
|
369
|
+
n
|
370
|
+
end
|
371
|
+
|
372
|
+
# Number of digits of precision
|
373
|
+
def precision
|
374
|
+
@precision
|
375
|
+
end
|
376
|
+
|
377
|
+
# Enables or disables the exact precision
|
378
|
+
def exact=(v)
|
379
|
+
@exact = v
|
380
|
+
update_precision
|
381
|
+
v
|
382
|
+
end
|
383
|
+
|
384
|
+
# Returns true if the precision is exact
|
385
|
+
def exact
|
386
|
+
@exact
|
387
|
+
end
|
388
|
+
|
389
|
+
# Returns true if the precision is exact
|
390
|
+
def exact?
|
391
|
+
@exact
|
392
|
+
end
|
393
|
+
|
394
|
+
# Alters the contexts by assigning options from a Hash. See Decimal#new() for the valid options.
|
395
|
+
def assign(options)
|
396
|
+
if options
|
397
|
+
@rounding = options[:rounding] unless options[:rounding].nil?
|
398
|
+
@precision = options[:precision] unless options[:precision].nil?
|
399
|
+
@traps = Decimal::Flags(options[:traps]) unless options[:traps].nil?
|
400
|
+
@flags = Decimal::Flags(options[:flags]) unless options[:flags].nil?
|
401
|
+
@ignored_flags = Decimal::Flags(options[:ignored_flags]) unless options[:ignored_flags].nil?
|
402
|
+
@emin = options[:emin] unless options[:emin].nil?
|
403
|
+
@emax = options[:emax] unless options[:emax].nil?
|
404
|
+
@capitals = options[:capitals ] unless options[:capitals ].nil?
|
405
|
+
@clamp = options[:clamp ] unless options[:clamp ].nil?
|
406
|
+
@exact = options[:exact ] unless options[:exact ].nil?
|
407
|
+
update_precision
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
attr_reader :coercible_type_handlers, :conversions
|
412
|
+
protected :coercible_type_handlers, :conversions
|
413
|
+
|
414
|
+
# Copy the state from other Context object.
|
415
|
+
def copy_from(other)
|
416
|
+
@rounding = other.rounding
|
417
|
+
@precision = other.precision
|
418
|
+
@traps = other.traps.dup
|
419
|
+
@flags = other.flags.dup
|
420
|
+
@ignored_flags = other.ignored_flags.dup
|
421
|
+
@emin = other.emin
|
422
|
+
@emax = other.emax
|
423
|
+
@capitals = other.capitals
|
424
|
+
@clamp = other.clamp
|
425
|
+
@exact = other.exact
|
426
|
+
@coercible_type_handlers = other.coercible_type_handlers.dup
|
427
|
+
@conversions = other.conversions.dup
|
428
|
+
end
|
429
|
+
|
430
|
+
def dup
|
431
|
+
Context.new(self)
|
432
|
+
end
|
433
|
+
|
434
|
+
CONDITION_MAP = {
|
435
|
+
#ConversionSyntax=>InvalidOperation,
|
436
|
+
#DivisionImpossible=>InvalidOperation,
|
437
|
+
DivisionUndefined=>InvalidOperation,
|
438
|
+
InvalidContext=>InvalidOperation
|
439
|
+
}
|
440
|
+
|
441
|
+
# Raises a flag (unless it is being ignores) and raises and
|
442
|
+
# exceptioin if the trap for it is enabled.
|
443
|
+
def exception(cond, msg='', *params)
|
444
|
+
err = (CONDITION_MAP[cond] || cond)
|
445
|
+
return err.handle(self, *params) if @ignored_flags[err]
|
446
|
+
@flags << err # @flags[err] = true
|
447
|
+
return cond.handle(self, *params) if !@traps[err]
|
448
|
+
raise err.new(*params), msg
|
449
|
+
end
|
450
|
+
|
451
|
+
# Addition of two decimal numbers
|
452
|
+
def add(x,y)
|
453
|
+
Decimal._convert(x).add(y,self)
|
454
|
+
end
|
455
|
+
|
456
|
+
# Subtraction of two decimal numbers
|
457
|
+
def subtract(x,y)
|
458
|
+
Decimal._convert(x).subtract(y,self)
|
459
|
+
end
|
460
|
+
|
461
|
+
# Multiplication of two decimal numbers
|
462
|
+
def multiply(x,y)
|
463
|
+
Decimal._convert(x).multiply(y,self)
|
464
|
+
end
|
465
|
+
|
466
|
+
# Division of two decimal numbers
|
467
|
+
def divide(x,y)
|
468
|
+
Decimal._convert(x).divide(y,self)
|
469
|
+
end
|
470
|
+
|
471
|
+
# Absolute value of a decimal number
|
472
|
+
def abs(x)
|
473
|
+
Decimal._convert(x).abs(self)
|
474
|
+
end
|
475
|
+
|
476
|
+
# Unary prefix plus operator
|
477
|
+
def plus(x)
|
478
|
+
Decimal._convert(x).plus(self)
|
479
|
+
end
|
480
|
+
|
481
|
+
# Unary prefix minus operator
|
482
|
+
def minus(x)
|
483
|
+
Decimal._convert(x)._neg(self)
|
484
|
+
end
|
485
|
+
|
486
|
+
# Converts a number to a string
|
487
|
+
def to_string(x, eng=false)
|
488
|
+
Decimal._convert(x)._fix(self).to_s(eng, self)
|
489
|
+
end
|
490
|
+
|
491
|
+
# Converts a number to a string, using scientific notation
|
492
|
+
def to_sci_string(x)
|
493
|
+
to_string x, false
|
494
|
+
end
|
495
|
+
|
496
|
+
# Converts a number to a string, using engineering notation
|
497
|
+
def to_eng_string(x)
|
498
|
+
to_string x, true
|
499
|
+
end
|
500
|
+
|
501
|
+
# Reduces an operand to its simplest form
|
502
|
+
# by removing trailing 0s and incrementing the exponent.
|
503
|
+
# (formerly called normalize in GDAS)
|
504
|
+
def reduce(x)
|
505
|
+
Decimal._convert(x).reduce(self)
|
506
|
+
end
|
507
|
+
|
508
|
+
# Adjusted exponent of x returned as a Decimal value.
|
509
|
+
def logb(x)
|
510
|
+
Decimal._convert(x).logb(self)
|
511
|
+
end
|
512
|
+
|
513
|
+
# Adds the second value to the exponent of the first: x*(radix**y)
|
514
|
+
#
|
515
|
+
# y must be an integer
|
516
|
+
def scaleb(x, y)
|
517
|
+
Decimal._convert(x).scaleb(y,self)
|
518
|
+
end
|
519
|
+
|
520
|
+
|
521
|
+
# Exponent in relation to the significand as an integer
|
522
|
+
# normalized to precision digits. (minimum exponent)
|
523
|
+
def normalized_integral_exponent(x)
|
524
|
+
x = Decimal._convert(x)
|
525
|
+
x.integral_exponent - (precision - x.number_of_digits)
|
526
|
+
end
|
527
|
+
|
528
|
+
# Significand normalized to precision digits
|
529
|
+
# x == normalized_integral_significand(x) * radix**(normalized_integral_exponent)
|
530
|
+
def normalized_integral_significand(x)
|
531
|
+
x = Decimal._convert(x)
|
532
|
+
x.integral_significand*(Decimal.int_radix_power(precision - x.number_of_digits))
|
533
|
+
end
|
534
|
+
|
535
|
+
# Returns both the (signed) normalized integral significand and the corresponding exponent
|
536
|
+
def to_normalized_int_scale(x)
|
537
|
+
x = Decimal._convert(x)
|
538
|
+
[x.sign*normalized_integral_significand(x), normalized_integral_exponent(x)]
|
539
|
+
end
|
540
|
+
|
541
|
+
# Is a normal number?
|
542
|
+
def normal?(x)
|
543
|
+
Decimal._convert(x).normal?(self)
|
544
|
+
end
|
545
|
+
|
546
|
+
# Is a subnormal number?
|
547
|
+
def subnormal?(x)
|
548
|
+
Decimal._convert(x).subnormal?(self)
|
549
|
+
end
|
550
|
+
|
551
|
+
# Classifies a number as one of
|
552
|
+
# 'sNaN', 'NaN', '-Infinity', '-Normal', '-Subnormal', '-Zero',
|
553
|
+
# '+Zero', '+Subnormal', '+Normal', '+Infinity'
|
554
|
+
def number_class(x)
|
555
|
+
Decimal._convert(x).number_class(self)
|
556
|
+
end
|
557
|
+
|
558
|
+
# Square root of a decimal number
|
559
|
+
def sqrt(x)
|
560
|
+
Decimal._convert(x).sqrt(self)
|
561
|
+
end
|
562
|
+
|
563
|
+
# Ruby-style integer division: (x/y).floor
|
564
|
+
def div(x,y)
|
565
|
+
Decimal._convert(x).div(y,self)
|
566
|
+
end
|
567
|
+
|
568
|
+
# Ruby-style modulo: x - y*div(x,y)
|
569
|
+
def modulo(x,y)
|
570
|
+
Decimal._convert(x).modulo(y,self)
|
571
|
+
end
|
572
|
+
|
573
|
+
# Ruby-style integer division and modulo: (x/y).floor, x - y*(x/y).floor
|
574
|
+
def divmod(x,y)
|
575
|
+
Decimal._convert(x).divmod(y,self)
|
576
|
+
end
|
577
|
+
|
578
|
+
# General Decimal Arithmetic Specification integer division: (x/y).truncate
|
579
|
+
def divide_int(x,y)
|
580
|
+
Decimal._convert(x).divide_int(y,self)
|
581
|
+
end
|
582
|
+
|
583
|
+
# General Decimal Arithmetic Specification remainder: x - y*divide_int(x,y)
|
584
|
+
def remainder(x,y)
|
585
|
+
Decimal._convert(x).remainder(y,self)
|
586
|
+
end
|
587
|
+
|
588
|
+
# General Decimal Arithmetic Specification remainder-near
|
589
|
+
# x - y*round_half_even(x/y)
|
590
|
+
def remainder_near(x,y)
|
591
|
+
Decimal._convert(x).remainder_near(y,self)
|
592
|
+
end
|
593
|
+
|
594
|
+
# General Decimal Arithmetic Specification integer division and remainder:
|
595
|
+
# (x/y).truncate, x - y*(x/y).truncate
|
596
|
+
def divrem(x,y)
|
597
|
+
Decimal._convert(x).divrem(y,self)
|
598
|
+
end
|
599
|
+
|
600
|
+
# Fused multiply-add.
|
601
|
+
#
|
602
|
+
# Computes (x*y+z) with no rounding of the intermediate product x*y.
|
603
|
+
def fma(x,y,z)
|
604
|
+
Decimal._convert(x).fma(y,z,self)
|
605
|
+
end
|
606
|
+
|
607
|
+
# Compares like <=> but returns a Decimal value.
|
608
|
+
# * -1 if x < y
|
609
|
+
# * 0 if x == b
|
610
|
+
# * +1 if x > y
|
611
|
+
# * NaN if x or y is NaN
|
612
|
+
def compare(x,y)
|
613
|
+
Decimal._convert(x).compare(y, self)
|
614
|
+
end
|
615
|
+
|
616
|
+
# Returns a copy of x with the sign set to +
|
617
|
+
def copy_abs(x)
|
618
|
+
Decimal._convert(x).copy_abs
|
619
|
+
end
|
620
|
+
|
621
|
+
# Returns a copy of x with the sign inverted
|
622
|
+
def copy_negate(x)
|
623
|
+
Decimal._convert(x).copy_negate
|
624
|
+
end
|
625
|
+
|
626
|
+
# Returns a copy of x with the sign of y
|
627
|
+
def copy_sign(x,y)
|
628
|
+
Decimal._convert(x).copy_sign(y)
|
629
|
+
end
|
630
|
+
|
631
|
+
# Rescale x so that the exponent is exp, either by padding with zeros
|
632
|
+
# or by truncating digits.
|
633
|
+
def rescale(x, exp, watch_exp=true)
|
634
|
+
Decimal._convert(x).rescale(exp, self, watch_exp)
|
635
|
+
end
|
636
|
+
|
637
|
+
# Quantize x so its exponent is the same as that of y.
|
638
|
+
def quantize(x, y, watch_exp=true)
|
639
|
+
Decimal._convert(x).quantize(y, self, watch_exp)
|
640
|
+
end
|
641
|
+
|
642
|
+
# Return true if x and y have the same exponent.
|
643
|
+
#
|
644
|
+
# If either operand is a special value, the following rules are used:
|
645
|
+
# * return true if both operands are infinities
|
646
|
+
# * return true if both operands are NaNs
|
647
|
+
# * otherwise, return false.
|
648
|
+
def same_quantum?(x,y)
|
649
|
+
Decimal._convert(x).same_quantum?(y)
|
650
|
+
end
|
651
|
+
|
652
|
+
# Rounds to a nearby integer.
|
653
|
+
#
|
654
|
+
# See also: Decimal#to_integral_value(), which does exactly the same as
|
655
|
+
# this method except that it doesn't raise Inexact or Rounded.
|
656
|
+
def to_integral_exact(x)
|
657
|
+
Decimal._convert(x).to_integral_exact(self)
|
658
|
+
end
|
659
|
+
|
660
|
+
# Rounds to a nearby integerwithout raising inexact, rounded.
|
661
|
+
#
|
662
|
+
# See also: Decimal#to_integral_exact(), which does exactly the same as
|
663
|
+
# this method except that it may raise Inexact or Rounded.
|
664
|
+
def to_integral_value(x)
|
665
|
+
Decimal._convert(x).to_integral_value(self)
|
666
|
+
end
|
667
|
+
|
668
|
+
# Returns the largest representable number smaller than x.
|
669
|
+
def next_minus(x)
|
670
|
+
Decimal._convert(x).next_minus(self)
|
671
|
+
end
|
672
|
+
|
673
|
+
# Returns the smallest representable number larger than x.
|
674
|
+
def next_plus(x)
|
675
|
+
Decimal._convert(x).next_plus(self)
|
676
|
+
end
|
677
|
+
|
678
|
+
# Returns the number closest to x, in the direction towards y.
|
679
|
+
#
|
680
|
+
# The result is the closest representable number to x
|
681
|
+
# (excluding x) that is in the direction towards y,
|
682
|
+
# unless both have the same value. If the two operands are
|
683
|
+
# numerically equal, then the result is a copy of x with the
|
684
|
+
# sign set to be the same as the sign of y.
|
685
|
+
def next_toward(x, y)
|
686
|
+
Decimal._convert(x).next_toward(y, self)
|
687
|
+
end
|
688
|
+
|
689
|
+
def to_s
|
690
|
+
inspect
|
691
|
+
end
|
692
|
+
|
693
|
+
def inspect
|
694
|
+
"<#{self.class}:\n" +
|
695
|
+
instance_variables.map { |v| " #{v}: #{eval(v)}"}.join("\n") +
|
696
|
+
">\n"
|
697
|
+
end
|
698
|
+
|
699
|
+
# Maximum integral significand value for numbers using this context's precision.
|
700
|
+
def maximum_significand
|
701
|
+
if exact?
|
702
|
+
exception(InvalidOperation, 'Exact maximum significand')
|
703
|
+
nil
|
704
|
+
else
|
705
|
+
Decimal.int_radix_power(precision)-1
|
706
|
+
end
|
707
|
+
end
|
708
|
+
|
709
|
+
# Maximum number of diagnostic digits in NaNs for numbers using this context's precision.
|
710
|
+
def maximum_nan_diagnostic_digits
|
711
|
+
if exact?
|
712
|
+
nil # ?
|
713
|
+
else
|
714
|
+
precision - (clamp ? 1 : 0)
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
# Internal use: array of numeric types that be coerced to Decimal.
|
719
|
+
def coercible_types
|
720
|
+
@coercible_type_handlers.keys
|
721
|
+
end
|
722
|
+
|
723
|
+
# Internal use: array of numeric types that be coerced to Decimal, including Decimal
|
724
|
+
def coercible_types_or_decimal
|
725
|
+
[Decimal] + coercible_types
|
726
|
+
end
|
727
|
+
|
728
|
+
# Internally used to convert numeric types to Decimal (or to an array [sign,coefficient,exponent])
|
729
|
+
def _coerce(x)
|
730
|
+
c = x.class
|
731
|
+
while c!=Object && (h=@coercible_type_handlers[c]).nil?
|
732
|
+
c = c.superclass
|
733
|
+
end
|
734
|
+
if h
|
735
|
+
h.call(x, self)
|
736
|
+
else
|
737
|
+
nil
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
# Define a numerical conversion from type to Decimal.
|
742
|
+
# The block that defines the conversion has two parameters: the value to be converted and the context and
|
743
|
+
# must return either a Decimal or [sign,coefficient,exponent]
|
744
|
+
def define_conversion_from(type, &blk)
|
745
|
+
@coercible_type_handlers[type] = blk
|
746
|
+
end
|
747
|
+
|
748
|
+
# Define a numerical conversion from Decimal to type as an instance method of Decimal
|
749
|
+
def define_conversion_to(type, &blk)
|
750
|
+
@conversions[type] = blk
|
751
|
+
end
|
752
|
+
|
753
|
+
# Convert a Decimal x to other numerical type
|
754
|
+
def convert_to(type, x)
|
755
|
+
converter = @conversions[type]
|
756
|
+
if converter.nil?
|
757
|
+
raise TypeError, "Undefined conversion from Decimal to #{type}."
|
758
|
+
elsif converter.is_a?(Symbol)
|
759
|
+
x.send converter
|
760
|
+
else
|
761
|
+
converter.call(x)
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
765
|
+
private
|
766
|
+
def update_precision
|
767
|
+
if @exact || @precision==0
|
768
|
+
@exact = true
|
769
|
+
@precision = 0
|
770
|
+
@traps << Inexact
|
771
|
+
@ignored_flags[Inexact] = false
|
772
|
+
else
|
773
|
+
@traps[Inexact] = false
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
end
|
778
|
+
|
779
|
+
# the DefaultContext is the base for new contexts; it can be changed.
|
780
|
+
DefaultContext = Decimal::Context.new(
|
781
|
+
:exact=>false, :precision=>28, :rounding=>:half_even,
|
782
|
+
:emin=> -999999999, :emax=>+999999999,
|
783
|
+
:flags=>[],
|
784
|
+
:traps=>[DivisionByZero, Overflow, InvalidOperation],
|
785
|
+
:ignored_flags=>[],
|
786
|
+
:capitals=>true,
|
787
|
+
:clamp=>true)
|
788
|
+
|
789
|
+
BasicContext = Decimal::Context.new(DefaultContext,
|
790
|
+
:precision=>9, :rounding=>:half_up,
|
791
|
+
:traps=>[DivisionByZero, Overflow, InvalidOperation, Clamped, Underflow],
|
792
|
+
:flags=>[])
|
793
|
+
|
794
|
+
ExtendedContext = Decimal::Context.new(DefaultContext,
|
795
|
+
:precision=>9, :rounding=>:half_even,
|
796
|
+
:traps=>[], :flags=>[], :clamp=>false)
|
797
|
+
|
798
|
+
# Context constructor; if an options hash is passed, the options are
|
799
|
+
# applied to the default context; if a Context is passed as the first
|
800
|
+
# argument, it is used as the base instead of the default context.
|
801
|
+
#
|
802
|
+
# See Context#new() for the valid options
|
803
|
+
def Decimal.Context(*args)
|
804
|
+
case args.size
|
805
|
+
when 0
|
806
|
+
base = DefaultContext
|
807
|
+
when 1
|
808
|
+
arg = args.first
|
809
|
+
if arg.instance_of?(Context)
|
810
|
+
base = arg
|
811
|
+
options = nil
|
812
|
+
elsif arg.instance_of?(Hash)
|
813
|
+
base = DefaultContext
|
814
|
+
options = arg
|
815
|
+
else
|
816
|
+
raise TypeError,"invalid argument for Decimal.Context"
|
817
|
+
end
|
818
|
+
when 2
|
819
|
+
base = args.first
|
820
|
+
options = args.last
|
821
|
+
else
|
822
|
+
raise ArgumentError,"wrong number of arguments (#{args.size} for 0, 1 or 2)"
|
823
|
+
end
|
824
|
+
|
825
|
+
if options.nil? || options.empty?
|
826
|
+
base
|
827
|
+
else
|
828
|
+
Context.new(base, options)
|
829
|
+
end
|
830
|
+
|
831
|
+
end
|
832
|
+
|
833
|
+
# Define a context by passing either of:
|
834
|
+
# * A Context object
|
835
|
+
# * A hash of options (or nothing) to alter a copy of the current context.
|
836
|
+
# * A Context object and a hash of options to alter a copy of it
|
837
|
+
def Decimal.define_context(*options)
|
838
|
+
context = options.shift if options.first.instance_of?(Context)
|
839
|
+
if context && options.empty?
|
840
|
+
context
|
841
|
+
else
|
842
|
+
context ||= Decimal.context
|
843
|
+
Context(context, *options)
|
844
|
+
end
|
845
|
+
end
|
846
|
+
|
847
|
+
# The current context (thread-local).
|
848
|
+
# If arguments are passed they are interpreted as in Decimal.define_context() to change
|
849
|
+
# the current context.
|
850
|
+
# If a block is given, this method is a synonym for Decimal.local_context().
|
851
|
+
def Decimal.context(*args, &blk)
|
852
|
+
if blk
|
853
|
+
# setup a local context
|
854
|
+
local_context(*args, &blk)
|
855
|
+
elsif args.empty?
|
856
|
+
# return the current context
|
857
|
+
Thread.current['Decimal.context'] ||= DefaultContext.dup
|
858
|
+
else
|
859
|
+
# change the current context
|
860
|
+
Decimal.context = define_context(*args)
|
861
|
+
end
|
862
|
+
end
|
863
|
+
|
864
|
+
# Change the current context (thread-local).
|
865
|
+
def Decimal.context=(c)
|
866
|
+
Thread.current['Decimal.context'] = c.dup
|
867
|
+
end
|
868
|
+
|
869
|
+
# Defines a scope with a local context. A context can be passed which will be
|
870
|
+
# set a the current context for the scope; also a hash can be passed with
|
871
|
+
# options to apply to the local scope.
|
872
|
+
# Changes done to the current context are reversed when the scope is exited.
|
873
|
+
def Decimal.local_context(*args)
|
874
|
+
keep = context.dup
|
875
|
+
Decimal.context = define_context(*args)
|
876
|
+
result = yield Decimal.context
|
877
|
+
Decimal.context = keep
|
878
|
+
result
|
879
|
+
end
|
880
|
+
|
881
|
+
# A decimal number with value zero and the specified sign
|
882
|
+
def Decimal.zero(sign=+1)
|
883
|
+
Decimal.new([sign, 0, 0])
|
884
|
+
end
|
885
|
+
|
886
|
+
# A decimal infinite number with the specified sign
|
887
|
+
def Decimal.infinity(sign=+1)
|
888
|
+
Decimal.new([sign, 0, :inf])
|
889
|
+
end
|
890
|
+
|
891
|
+
# A decimal NaN (not a number)
|
892
|
+
def Decimal.nan()
|
893
|
+
Decimal.new([+1, nil, :nan])
|
894
|
+
end
|
895
|
+
|
896
|
+
#--
|
897
|
+
# =Notes on the representation of Decimal numbers.
|
898
|
+
#
|
899
|
+
# @sign is +1 for plus and -1 for minus
|
900
|
+
# @coeff is the integral significand stored as an integer (so leading zeros cannot be kept)
|
901
|
+
# @exp is the exponent to be applied to @coeff as an integer or one of :inf, :nan, :snan for special values
|
902
|
+
#
|
903
|
+
# The Python Decimal representation has these slots:
|
904
|
+
# _sign is 1 for minus, 0 for plus
|
905
|
+
# _int is the integral significand as a string of digits (leading zeroes are not kept)
|
906
|
+
# _exp is the exponent as an integer or 'F' for infinity, 'n' for NaN , 'N' for sNaN
|
907
|
+
# _is_especial is true for special values (infinity, NaN, sNaN)
|
908
|
+
# An additional class _WorkRep is used in Python for non-special decimal values with:
|
909
|
+
# sign
|
910
|
+
# int (significand as an integer)
|
911
|
+
# exp
|
912
|
+
#
|
913
|
+
# =Exponent values
|
914
|
+
#
|
915
|
+
# In GDAS (General Decimal Arithmetic Specification) numbers are represented by an unnormalized integral
|
916
|
+
# significand and an exponent (also called 'scale'.)
|
917
|
+
#
|
918
|
+
# The reduce operation (originally called 'normalize') removes trailing 0s and increments the exponent if necessary;
|
919
|
+
# the representation is rescaled to use the maximum exponent possible (while maintaining an integral significand.)
|
920
|
+
#
|
921
|
+
# A classical floating-point normalize opration would remove leading 0s and decrement the exponent instead,
|
922
|
+
# rescaling to the minimum exponent theat maintains the significand value under some conventional limit (1 or the radix).
|
923
|
+
#
|
924
|
+
# The logb and adjusted operations return the exponent that applies to the most significand digit (logb as a Decimal
|
925
|
+
# and adjusted as an integer.) This is the normalized scientific exponent.
|
926
|
+
#
|
927
|
+
# The most common normalized exponent is the normalized integral exponent for a fixed number of precision digits.
|
928
|
+
#
|
929
|
+
# The normalized fractional exponent is what BigDecima#exponent returns.
|
930
|
+
#
|
931
|
+
# ==Relations between exponent values
|
932
|
+
#
|
933
|
+
# The number of (kept) significand digits is s = a - e + 1
|
934
|
+
# where a is the adjusted exponent and e is the internal exponent (the unnormalized integral exponent.)
|
935
|
+
#
|
936
|
+
# The number of significant digits (excluding leading and trailing zeroes) is sr = a - re + 1
|
937
|
+
# where re is the internal exponent of the reduced value.
|
938
|
+
#
|
939
|
+
# The normalized integral exponent is e - (p - s) = a - p + 1
|
940
|
+
# where p is the fixed precision.
|
941
|
+
#
|
942
|
+
# The normalized fractional exponent is e + s = a + 1
|
943
|
+
#
|
944
|
+
# ==Example: 0.01204
|
945
|
+
#
|
946
|
+
# * The integral significand is 120400 and the internal exponent that applies to it is e = -7
|
947
|
+
# * The number of significand digits is s = 6
|
948
|
+
# * The reduced representation is 1204 with internal exponent re = -5
|
949
|
+
# * The number of significant digits sr = 4
|
950
|
+
# * The adjusted exponent is a = -2 (the adjusted representation is 1.204 with exponent -2)
|
951
|
+
# * Given a precision p = 8, the normalized integral representation is 12040000 with exponent -9
|
952
|
+
# * The normalized fractional representation is 0.1204 with exponent -1
|
953
|
+
#
|
954
|
+
# =Interoperatibility with other numeric types
|
955
|
+
#
|
956
|
+
# For some numeric types implicit conversion to Decimal is defined through these methods:
|
957
|
+
# * Decimal#coerce() is used when a Decimal is the right hand of an operator
|
958
|
+
# and the left hand is another numeric type
|
959
|
+
# * Decimal#_bin_op() used internally to define binary operators and use the Ruby coerce protocol:
|
960
|
+
# if the right-hand operand is of known type it is converted with Decimal; otherwise use coerce
|
961
|
+
# * Decimal._convert() converts known types to Decimal with Decimal() or raises an exception.
|
962
|
+
# * Decimal() casts known types and text representations of numbers to Decimal using the constructor.
|
963
|
+
# * Decimal#initialize performs the actual type conversion
|
964
|
+
#
|
965
|
+
# The known or 'coercible' types are initially Integer and Rational, but this can be extended to
|
966
|
+
# other types using define_conversion_from() in a Context object.
|
967
|
+
#++
|
968
|
+
|
969
|
+
# A decimal value can be defined by:
|
970
|
+
# * A String containing a text representation of the number
|
971
|
+
# * An Integer
|
972
|
+
# * A Rational
|
973
|
+
# * Another Decimal value.
|
974
|
+
# * A sign, coefficient and exponent (either as separate arguments, as an array or as a Hash with symbolic keys).
|
975
|
+
# This is the internal representation of Decimal, as returned by Decimal#split.
|
976
|
+
# The sign is +1 for plus and -1 for minus; the coefficient and exponent are
|
977
|
+
# integers, except for special values which are defined by :inf, :nan or :snan for the exponent.
|
978
|
+
# An optional Context can be passed as the last argument to override the current context; also a hash can be passed
|
979
|
+
# to override specific context parameters.
|
980
|
+
# The Decimal() admits the same parameters and can be used as a shortcut for Decimal creation.
|
981
|
+
def initialize(*args)
|
982
|
+
context = nil
|
983
|
+
if args.size>0 && args.last.instance_of?(Context)
|
984
|
+
context ||= args.pop
|
985
|
+
elsif args.size>1 && args.last.instance_of?(Hash)
|
986
|
+
context ||= args.pop
|
987
|
+
elsif args.size==1 && args.last.instance_of?(Hash)
|
988
|
+
arg = args.last
|
989
|
+
args = [arg[:sign], args[:coefficient], args[:exponent]]
|
990
|
+
arg.delete :sign
|
991
|
+
arg.delete :coefficient
|
992
|
+
arg.delete :exponent
|
993
|
+
context ||= arg
|
994
|
+
end
|
995
|
+
|
996
|
+
context = Decimal.define_context(context)
|
997
|
+
|
998
|
+
case args.size
|
999
|
+
when 3
|
1000
|
+
@sign, @coeff, @exp = args
|
1001
|
+
# TO DO: validate
|
1002
|
+
|
1003
|
+
when 1
|
1004
|
+
arg = args.first
|
1005
|
+
case arg
|
1006
|
+
|
1007
|
+
when Decimal
|
1008
|
+
@sign, @coeff, @exp = arg.split
|
1009
|
+
|
1010
|
+
when *context.coercible_types
|
1011
|
+
v = context._coerce(arg)
|
1012
|
+
@sign, @coeff, @exp = v.is_a?(Decimal) ? v.split : v
|
1013
|
+
|
1014
|
+
when String
|
1015
|
+
if arg.strip != arg
|
1016
|
+
@sign,@coeff,@exp = context.exception(ConversionSyntax, "no trailing or leading whitespace is permitted").split
|
1017
|
+
return
|
1018
|
+
end
|
1019
|
+
m = _parser(arg)
|
1020
|
+
if m.nil?
|
1021
|
+
@sign,@coeff,@exp = context.exception(ConversionSyntax, "Invalid literal for Decimal: #{arg.inspect}").split
|
1022
|
+
return
|
1023
|
+
end
|
1024
|
+
@sign = (m.sign == '-') ? -1 : +1
|
1025
|
+
if m.int || m.onlyfrac
|
1026
|
+
if m.int
|
1027
|
+
intpart = m.int
|
1028
|
+
fracpart = m.frac
|
1029
|
+
else
|
1030
|
+
intpart = ''
|
1031
|
+
fracpart = m.onlyfrac
|
1032
|
+
end
|
1033
|
+
@exp = m.exp.to_i
|
1034
|
+
if fracpart
|
1035
|
+
@coeff = (intpart+fracpart).to_i
|
1036
|
+
@exp -= fracpart.size
|
1037
|
+
else
|
1038
|
+
@coeff = intpart.to_i
|
1039
|
+
end
|
1040
|
+
else
|
1041
|
+
if m.diag
|
1042
|
+
# NaN
|
1043
|
+
@coeff = (m.diag.nil? || m.diag.empty?) ? nil : m.diag.to_i
|
1044
|
+
@coeff = nil if @coeff==0
|
1045
|
+
if @coeff
|
1046
|
+
max_diag_len = context.maximum_nan_diagnostic_digits
|
1047
|
+
if max_diag_len && @coeff >= Decimal.int_radix_power(max_diag_len)
|
1048
|
+
@sign,@coeff,@exp = context.exception(ConversionSyntax, "diagnostic info too long in NaN").split
|
1049
|
+
return
|
1050
|
+
end
|
1051
|
+
end
|
1052
|
+
@exp = m.signal ? :snan : :nan
|
1053
|
+
else
|
1054
|
+
# Infinity
|
1055
|
+
@coeff = 0
|
1056
|
+
@exp = :inf
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
when Array
|
1060
|
+
@sign, @coeff, @exp = arg
|
1061
|
+
else
|
1062
|
+
raise TypeError, "invalid argument #{arg.inspect}"
|
1063
|
+
end
|
1064
|
+
else
|
1065
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 1 or 3)"
|
1066
|
+
end
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
# Returns the internal representation of the number, composed of:
|
1070
|
+
# * a sign which is +1 for plus and -1 for minus
|
1071
|
+
# * a coefficient (significand) which is an integer
|
1072
|
+
# * an exponent (an integer) or :inf, :nan or :snan for special values
|
1073
|
+
# The value of non-special numbers is sign*coefficient*10^exponent
|
1074
|
+
def split
|
1075
|
+
[@sign, @coeff, @exp]
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
# Returns whether the number is a special value (NaN or Infinity).
|
1079
|
+
def special?
|
1080
|
+
@exp.instance_of?(Symbol)
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
# Returns whether the number is not actualy one (NaN, not a number).
|
1084
|
+
def nan?
|
1085
|
+
@exp==:nan || @exp==:snan
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
# Returns whether the number is a quite NaN (non-signaling)
|
1089
|
+
def qnan?
|
1090
|
+
@exp == :nan
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
# Returns whether the number is a signaling NaN
|
1094
|
+
def snan?
|
1095
|
+
@exp == :snan
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
# Returns whether the number is infinite
|
1099
|
+
def infinite?
|
1100
|
+
@exp == :inf
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
# Returns whether the number is finite
|
1104
|
+
def finite?
|
1105
|
+
!special?
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
# Returns whether the number is zero
|
1109
|
+
def zero?
|
1110
|
+
@coeff==0 && !special?
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
# Returns whether the number not zero
|
1114
|
+
def nonzero?
|
1115
|
+
special? || @coeff>0
|
1116
|
+
end
|
1117
|
+
|
1118
|
+
# Returns whether the number is subnormal
|
1119
|
+
def subnormal?(context=nil)
|
1120
|
+
return false if special? || zero?
|
1121
|
+
context = Decimal.define_context(context)
|
1122
|
+
self.adjusted_exponent < context.emin
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
# Returns whether the number is normal
|
1126
|
+
def normal?(context=nil)
|
1127
|
+
return true if special? || zero?
|
1128
|
+
context = Decimal.define_context(context)
|
1129
|
+
(context.emin <= self.adjusted_exponent) && (self.adjusted_exponent <= context.emax)
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
# Classifies a number as one of
|
1133
|
+
# 'sNaN', 'NaN', '-Infinity', '-Normal', '-Subnormal', '-Zero',
|
1134
|
+
# '+Zero', '+Subnormal', '+Normal', '+Infinity'
|
1135
|
+
def number_class(context=nil)
|
1136
|
+
return "sNaN" if snan?
|
1137
|
+
return "NaN" if nan?
|
1138
|
+
if infinite?
|
1139
|
+
return '+Infinity' if @sign==+1
|
1140
|
+
return '-Infinity' # if @sign==-1
|
1141
|
+
end
|
1142
|
+
if zero?
|
1143
|
+
return '+Zero' if @sign==+1
|
1144
|
+
return '-Zero' # if @sign==-1
|
1145
|
+
end
|
1146
|
+
context = Decimal.define_context(context)
|
1147
|
+
if subnormal?(context)
|
1148
|
+
return '+Subnormal' if @sign==+1
|
1149
|
+
return '-Subnormal' # if @sign==-1
|
1150
|
+
end
|
1151
|
+
return '+Normal' if @sign==+1
|
1152
|
+
return '-Normal' if @sign==-1
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
# Used internally to convert numbers to be used in an operation to a suitable numeric type
|
1156
|
+
def coerce(other)
|
1157
|
+
case other
|
1158
|
+
when *Decimal.context.coercible_types_or_decimal
|
1159
|
+
[Decimal(other),self]
|
1160
|
+
else
|
1161
|
+
super
|
1162
|
+
end
|
1163
|
+
end
|
1164
|
+
|
1165
|
+
# Used internally to define binary operators
|
1166
|
+
def _bin_op(op, meth, other, context=nil)
|
1167
|
+
context = Decimal.define_context(context)
|
1168
|
+
case other
|
1169
|
+
when *context.coercible_types_or_decimal
|
1170
|
+
self.send meth, Decimal(other, context), context
|
1171
|
+
else
|
1172
|
+
x, y = other.coerce(self)
|
1173
|
+
x.send op, y
|
1174
|
+
end
|
1175
|
+
end
|
1176
|
+
private :_bin_op
|
1177
|
+
|
1178
|
+
# Unary minus operator
|
1179
|
+
def -@(context=nil)
|
1180
|
+
#(context || Decimal.context).minus(self)
|
1181
|
+
_neg(context)
|
1182
|
+
end
|
1183
|
+
|
1184
|
+
# Unary plus operator
|
1185
|
+
def +@(context=nil)
|
1186
|
+
#(context || Decimal.context).plus(self)
|
1187
|
+
_pos(context)
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
# Addition of two decimal numbers
|
1191
|
+
def +(other, context=nil)
|
1192
|
+
_bin_op :+, :add, other, context
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
# Subtraction of two decimal numbers
|
1196
|
+
def -(other, context=nil)
|
1197
|
+
_bin_op :-, :subtract, other, context
|
1198
|
+
end
|
1199
|
+
|
1200
|
+
# Multiplication of two decimal numbers
|
1201
|
+
def *(other, context=nil)
|
1202
|
+
_bin_op :*, :multiply, other, context
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
# Division of two decimal numbers
|
1206
|
+
def /(other, context=nil)
|
1207
|
+
_bin_op :/, :divide, other, context
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
# Modulo of two decimal numbers
|
1211
|
+
def %(other, context=nil)
|
1212
|
+
_bin_op :%, :modulo, other, context
|
1213
|
+
end
|
1214
|
+
|
1215
|
+
# Addition
|
1216
|
+
def add(other, context=nil)
|
1217
|
+
|
1218
|
+
context = Decimal.define_context(context)
|
1219
|
+
other = Decimal._convert(other)
|
1220
|
+
|
1221
|
+
if self.special? || other.special?
|
1222
|
+
ans = _check_nans(context,other)
|
1223
|
+
return ans if ans
|
1224
|
+
|
1225
|
+
if self.infinite?
|
1226
|
+
if self.sign != other.sign && other.infinite?
|
1227
|
+
return context.exception(InvalidOperation, '-INF + INF')
|
1228
|
+
end
|
1229
|
+
return Decimal(self)
|
1230
|
+
end
|
1231
|
+
|
1232
|
+
return Decimal(other) if other.infinite?
|
1233
|
+
end
|
1234
|
+
|
1235
|
+
exp = [self.integral_exponent, other.integral_exponent].min
|
1236
|
+
negativezero = (context.rounding == ROUND_FLOOR && self.sign != other.sign)
|
1237
|
+
|
1238
|
+
if self.zero? && other.zero?
|
1239
|
+
sign = [self.sign, other.sign].max
|
1240
|
+
sign = -1 if negativezero
|
1241
|
+
ans = Decimal.new([sign, 0, exp])._fix(context)
|
1242
|
+
return ans
|
1243
|
+
end
|
1244
|
+
|
1245
|
+
if self.zero?
|
1246
|
+
exp = [exp, other.integral_exponent - context.precision - 1].max unless context.exact?
|
1247
|
+
return other._rescale(exp, context.rounding)._fix(context)
|
1248
|
+
end
|
1249
|
+
|
1250
|
+
if other.zero?
|
1251
|
+
exp = [exp, self.integral_exponent - context.precision - 1].max unless context.exact?
|
1252
|
+
return self._rescale(exp, context.rounding)._fix(context)
|
1253
|
+
end
|
1254
|
+
|
1255
|
+
op1, op2 = Decimal._normalize(self, other, context.precision)
|
1256
|
+
|
1257
|
+
result_sign = result_coeff = result_exp = nil
|
1258
|
+
if op1.sign != op2.sign
|
1259
|
+
return ans = Decimal.new([negativezero ? -1 : +1, 0, exp])._fix(context) if op1.integral_significand == op2.integral_significand
|
1260
|
+
op1,op2 = op2,op1 if op1.integral_significand < op2.integral_significand
|
1261
|
+
result_sign = op1.sign
|
1262
|
+
op1,op2 = op1.copy_negate, op2.copy_negate if result_sign < 0
|
1263
|
+
elsif op1.sign < 0
|
1264
|
+
result_sign = -1
|
1265
|
+
op1,op2 = op1.copy_negate, op2.copy_negate
|
1266
|
+
else
|
1267
|
+
result_sign = +1
|
1268
|
+
end
|
1269
|
+
|
1270
|
+
#puts "op1=#{op1.inspect} op2=#{op2.inspect}"
|
1271
|
+
|
1272
|
+
|
1273
|
+
if op2.sign == +1
|
1274
|
+
result_coeff = op1.integral_significand + op2.integral_significand
|
1275
|
+
else
|
1276
|
+
result_coeff = op1.integral_significand - op2.integral_significand
|
1277
|
+
end
|
1278
|
+
|
1279
|
+
result_exp = op1.integral_exponent
|
1280
|
+
|
1281
|
+
#puts "->#{Decimal([result_sign, result_coeff, result_exp]).inspect}"
|
1282
|
+
|
1283
|
+
return Decimal([result_sign, result_coeff, result_exp])._fix(context)
|
1284
|
+
|
1285
|
+
end
|
1286
|
+
|
1287
|
+
|
1288
|
+
# Subtraction
|
1289
|
+
def subtract(other, context=nil)
|
1290
|
+
|
1291
|
+
context = Decimal.define_context(context)
|
1292
|
+
other = Decimal._convert(other)
|
1293
|
+
|
1294
|
+
if self.special? || other.special?
|
1295
|
+
ans = _check_nans(context,other)
|
1296
|
+
return ans if ans
|
1297
|
+
end
|
1298
|
+
return add(other.copy_negate, context)
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
# Multiplication
|
1302
|
+
def multiply(other, context=nil)
|
1303
|
+
context = Decimal.define_context(context)
|
1304
|
+
other = Decimal._convert(other)
|
1305
|
+
resultsign = self.sign * other.sign
|
1306
|
+
if self.special? || other.special?
|
1307
|
+
ans = _check_nans(context,other)
|
1308
|
+
return ans if ans
|
1309
|
+
|
1310
|
+
if self.infinite?
|
1311
|
+
return context.exception(InvalidOperation,"(+-)INF * 0") if other.zero?
|
1312
|
+
return Decimal.infinity(resultsign)
|
1313
|
+
end
|
1314
|
+
if other.infinite?
|
1315
|
+
return context.exception(InvalidOperation,"0 * (+-)INF") if self.zero?
|
1316
|
+
return Decimal.infinity(resultsign)
|
1317
|
+
end
|
1318
|
+
end
|
1319
|
+
|
1320
|
+
resultexp = self.integral_exponent + other.integral_exponent
|
1321
|
+
|
1322
|
+
return Decimal([resultsign, 0, resultexp])._fix(context) if self.zero? || other.zero?
|
1323
|
+
#return Decimal([resultsign, other.integral_significand, resultexp])._fix(context) if self.integral_significand==1
|
1324
|
+
#return Decimal([resultsign, self.integral_significand, resultexp])._fix(context) if other.integral_significand==1
|
1325
|
+
|
1326
|
+
return Decimal([resultsign, other.integral_significand*self.integral_significand, resultexp])._fix(context)
|
1327
|
+
|
1328
|
+
end
|
1329
|
+
|
1330
|
+
# Division
|
1331
|
+
def divide(other, context=nil)
|
1332
|
+
context = Decimal.define_context(context)
|
1333
|
+
other = Decimal._convert(other)
|
1334
|
+
resultsign = self.sign * other.sign
|
1335
|
+
if self.special? || other.special?
|
1336
|
+
ans = _check_nans(context,other)
|
1337
|
+
return ans if ans
|
1338
|
+
if self.infinite?
|
1339
|
+
return context.exception(InvalidOperation,"(+-)INF/(+-)INF") if other.infinite?
|
1340
|
+
return Decimal.infinity(resultsign)
|
1341
|
+
end
|
1342
|
+
if other.infinite?
|
1343
|
+
context.exception(Clamped,"Division by infinity")
|
1344
|
+
return Decimal.new([resultsign, 0, context.etiny])
|
1345
|
+
end
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
if other.zero?
|
1349
|
+
return context.exception(DivisionUndefined, '0 / 0') if self.zero?
|
1350
|
+
return context.exception(DivisionByZero, 'x / 0', resultsign)
|
1351
|
+
end
|
1352
|
+
|
1353
|
+
if self.zero?
|
1354
|
+
exp = self.integral_exponent - other.integral_exponent
|
1355
|
+
coeff = 0
|
1356
|
+
else
|
1357
|
+
prec = context.exact? ? self.number_of_digits + 4*other.number_of_digits : context.precision # this assumes radix==10
|
1358
|
+
shift = other.number_of_digits - self.number_of_digits + prec + 1
|
1359
|
+
exp = self.integral_exponent - other.integral_exponent - shift
|
1360
|
+
if shift >= 0
|
1361
|
+
coeff, remainder = (self.integral_significand*Decimal.int_radix_power(shift)).divmod(other.integral_significand)
|
1362
|
+
else
|
1363
|
+
coeff, remainder = self.integral_significand.divmod(other.integral_significand*Decimal.int_radix_power(-shift))
|
1364
|
+
end
|
1365
|
+
if remainder != 0
|
1366
|
+
return context.exception(Inexact) if context.exact?
|
1367
|
+
coeff += 1 if (coeff%(Decimal.radix/2)) == 0
|
1368
|
+
else
|
1369
|
+
ideal_exp = self.integral_exponent - other.integral_exponent
|
1370
|
+
while (exp < ideal_exp) && ((coeff % Decimal.radix)==0)
|
1371
|
+
coeff /= Decimal.radix
|
1372
|
+
exp += 1
|
1373
|
+
end
|
1374
|
+
end
|
1375
|
+
|
1376
|
+
end
|
1377
|
+
return Decimal([resultsign, coeff, exp])._fix(context)
|
1378
|
+
|
1379
|
+
end
|
1380
|
+
|
1381
|
+
# Absolute value
|
1382
|
+
def abs(context=nil)
|
1383
|
+
if special?
|
1384
|
+
ans = _check_nans(context)
|
1385
|
+
return ans if ans
|
1386
|
+
end
|
1387
|
+
sign<0 ? _neg(context) : _pos(context)
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
# Unary prefix plus operator
|
1391
|
+
def plus(context=nil)
|
1392
|
+
_pos(context)
|
1393
|
+
end
|
1394
|
+
|
1395
|
+
# Unary prefix minus operator
|
1396
|
+
def minus(context=nil)
|
1397
|
+
_neg(context)
|
1398
|
+
end
|
1399
|
+
|
1400
|
+
# Largest representable number smaller than itself
|
1401
|
+
def next_minus(context=nil)
|
1402
|
+
context = Decimal.define_context(context)
|
1403
|
+
if special?
|
1404
|
+
ans = _check_nans(context)
|
1405
|
+
return ans if ans
|
1406
|
+
if infinite?
|
1407
|
+
return Decimal.new(self) if @sign == -1
|
1408
|
+
# @sign == +1
|
1409
|
+
if context.exact?
|
1410
|
+
return context.exception(InvalidOperation, 'Exact +INF next minus')
|
1411
|
+
else
|
1412
|
+
return Decimal.new(+1, context.maximum_significand, context.etop)
|
1413
|
+
end
|
1414
|
+
end
|
1415
|
+
end
|
1416
|
+
|
1417
|
+
result = nil
|
1418
|
+
Decimal.local_context(context) do |local|
|
1419
|
+
local.rounding = :floor
|
1420
|
+
local.ignore_all_flags
|
1421
|
+
result = self._fix(local)
|
1422
|
+
if result == self
|
1423
|
+
result = self - Decimal(+1, 1, local.etiny-1)
|
1424
|
+
end
|
1425
|
+
end
|
1426
|
+
result
|
1427
|
+
end
|
1428
|
+
|
1429
|
+
# Smallest representable number larger than itself
|
1430
|
+
def next_plus(context=nil)
|
1431
|
+
context = Decimal.define_context(context)
|
1432
|
+
if special?
|
1433
|
+
ans = _check_nans(context)
|
1434
|
+
return ans if ans
|
1435
|
+
if infinite?
|
1436
|
+
return Decimal.new(self) if @sign == +1
|
1437
|
+
# @sign == -1
|
1438
|
+
if context.exact?
|
1439
|
+
return context.exception(InvalidOperation, 'Exact -INF next plus')
|
1440
|
+
else
|
1441
|
+
return Decimal.new(-1, context.maximum_significand, context.etop)
|
1442
|
+
end
|
1443
|
+
end
|
1444
|
+
end
|
1445
|
+
|
1446
|
+
result = nil
|
1447
|
+
Decimal.local_context(context) do |local|
|
1448
|
+
local.rounding = :ceiling
|
1449
|
+
local.ignore_all_flags
|
1450
|
+
result = self._fix(local)
|
1451
|
+
if result == self
|
1452
|
+
result = self + Decimal(+1, 1, local.etiny-1)
|
1453
|
+
end
|
1454
|
+
end
|
1455
|
+
result
|
1456
|
+
|
1457
|
+
end
|
1458
|
+
|
1459
|
+
# Returns the number closest to self, in the direction towards other.
|
1460
|
+
def next_toward(other, context=nil)
|
1461
|
+
context = Decimal.define_context(context)
|
1462
|
+
other = Decimal._convert(other)
|
1463
|
+
ans = _check_nans(context,other)
|
1464
|
+
return ans if ans
|
1465
|
+
|
1466
|
+
comparison = self <=> other
|
1467
|
+
return self.copy_sign(other) if comparison == 0
|
1468
|
+
|
1469
|
+
if comparison == -1
|
1470
|
+
result = self.next_plus(context)
|
1471
|
+
else # comparison == 1
|
1472
|
+
result = self.next_minus(context)
|
1473
|
+
end
|
1474
|
+
|
1475
|
+
# decide which flags to raise using value of ans
|
1476
|
+
if result.infinite?
|
1477
|
+
context.exception Overflow, 'Infinite result from next_toward', result.sign
|
1478
|
+
context.exception Rounded
|
1479
|
+
context.exception Inexact
|
1480
|
+
elsif result.adjusted_exponent < context.emin
|
1481
|
+
context.exception Underflow
|
1482
|
+
context.exception Subnormal
|
1483
|
+
context.exception Rounded
|
1484
|
+
context.exception Inexact
|
1485
|
+
# if precision == 1 then we don't raise Clamped for a
|
1486
|
+
# result 0E-etiny.
|
1487
|
+
context.exception Clamped if result.zero?
|
1488
|
+
end
|
1489
|
+
|
1490
|
+
result
|
1491
|
+
end
|
1492
|
+
|
1493
|
+
# Square root
|
1494
|
+
def sqrt(context=nil)
|
1495
|
+
context = Decimal.define_context(context)
|
1496
|
+
if special?
|
1497
|
+
ans = _check_nans(context)
|
1498
|
+
return ans if ans
|
1499
|
+
return Decimal.new(self) if infinite? && @sign==+1
|
1500
|
+
end
|
1501
|
+
return Decimal.new([@sign, 0, @exp/2])._fix(context) if zero?
|
1502
|
+
return context.exception(InvalidOperation, 'sqrt(-x), x>0') if @sign<0
|
1503
|
+
prec = context.precision + 1
|
1504
|
+
e = (@exp >> 1)
|
1505
|
+
if (@exp & 1)!=0
|
1506
|
+
c = @coeff*Decimal.radix
|
1507
|
+
l = (number_of_digits >> 1) + 1
|
1508
|
+
else
|
1509
|
+
c = @coeff
|
1510
|
+
l = (number_of_digits+1) >> 1
|
1511
|
+
end
|
1512
|
+
shift = prec - l
|
1513
|
+
if shift >= 0
|
1514
|
+
c = Decimal.int_mult_radix_power(c, (shift<<1))
|
1515
|
+
exact = true
|
1516
|
+
else
|
1517
|
+
c, remainder = c.divmod(Decimal.int_radix_power((-shift)<<1))
|
1518
|
+
exact = (remainder==0)
|
1519
|
+
end
|
1520
|
+
e -= shift
|
1521
|
+
|
1522
|
+
n = Decimal.int_radix_power(prec)
|
1523
|
+
while true
|
1524
|
+
q = c / n
|
1525
|
+
break if n <= q
|
1526
|
+
n = ((n + q) >> 1)
|
1527
|
+
end
|
1528
|
+
exact = exact && (n*n == c)
|
1529
|
+
|
1530
|
+
if exact
|
1531
|
+
if shift >= 0
|
1532
|
+
n = Decimal.int_div_radix_power(n, shift)
|
1533
|
+
else
|
1534
|
+
n = Decimal.int_mult_radix_power(n, -shift)
|
1535
|
+
end
|
1536
|
+
e += shift
|
1537
|
+
else
|
1538
|
+
return context.exception(Inexact) if context.exact?
|
1539
|
+
n += 1 if (n%5)==0
|
1540
|
+
end
|
1541
|
+
ans = Decimal.new([+1,n,e])
|
1542
|
+
Decimal.local_context(:rounding=>:half_even) do
|
1543
|
+
ans = ans._fix(context)
|
1544
|
+
end
|
1545
|
+
return ans
|
1546
|
+
end
|
1547
|
+
|
1548
|
+
# General Decimal Arithmetic Specification integer division and remainder:
|
1549
|
+
# (x/y).truncate, x - y*(x/y).truncate
|
1550
|
+
def divrem(other, context=nil)
|
1551
|
+
context = Decimal.define_context(context)
|
1552
|
+
other = Decimal._convert(other)
|
1553
|
+
|
1554
|
+
ans = _check_nans(context,other)
|
1555
|
+
return [ans,ans] if ans
|
1556
|
+
|
1557
|
+
sign = self.sign * other.sign
|
1558
|
+
|
1559
|
+
if self.infinite?
|
1560
|
+
if other.infinite?
|
1561
|
+
ans = context.exception(InvalidOperation, 'divmod(INF,INF)')
|
1562
|
+
return [ans,ans]
|
1563
|
+
else
|
1564
|
+
return [Decimal.infinity(sign), context.exception(InvalidOperation, 'INF % x')]
|
1565
|
+
end
|
1566
|
+
end
|
1567
|
+
|
1568
|
+
if other.zero?
|
1569
|
+
if self.zero?
|
1570
|
+
ans = context.exception(DivisionUndefined, 'divmod(0,0)')
|
1571
|
+
return [ans,ans]
|
1572
|
+
else
|
1573
|
+
return [context.exception(DivisionByZero, 'x // 0', sign),
|
1574
|
+
context.exception(InvalidOperation, 'x % 0')]
|
1575
|
+
end
|
1576
|
+
end
|
1577
|
+
|
1578
|
+
quotient, remainder = self._divide_truncate(other, context)
|
1579
|
+
return [quotient, remainder._fix(context)]
|
1580
|
+
end
|
1581
|
+
|
1582
|
+
# Ruby-style integer division and modulo: (x/y).floor, x - y*(x/y).floor
|
1583
|
+
def divmod(other, context=nil)
|
1584
|
+
context = Decimal.define_context(context)
|
1585
|
+
other = Decimal._convert(other)
|
1586
|
+
|
1587
|
+
ans = _check_nans(context,other)
|
1588
|
+
return [ans,ans] if ans
|
1589
|
+
|
1590
|
+
sign = self.sign * other.sign
|
1591
|
+
|
1592
|
+
if self.infinite?
|
1593
|
+
if other.infinite?
|
1594
|
+
ans = context.exception(InvalidOperation, 'divmod(INF,INF)')
|
1595
|
+
return [ans,ans]
|
1596
|
+
else
|
1597
|
+
return [Decimal.infinity(sign), context.exception(InvalidOperation, 'INF % x')]
|
1598
|
+
end
|
1599
|
+
end
|
1600
|
+
|
1601
|
+
if other.zero?
|
1602
|
+
if self.zero?
|
1603
|
+
ans = context.exception(DivisionUndefined, 'divmod(0,0)')
|
1604
|
+
return [ans,ans]
|
1605
|
+
else
|
1606
|
+
return [context.exception(DivisionByZero, 'x // 0', sign),
|
1607
|
+
context.exception(InvalidOperation, 'x % 0')]
|
1608
|
+
end
|
1609
|
+
end
|
1610
|
+
|
1611
|
+
quotient, remainder = self._divide_floor(other, context)
|
1612
|
+
return [quotient, remainder._fix(context)]
|
1613
|
+
end
|
1614
|
+
|
1615
|
+
|
1616
|
+
# General Decimal Arithmetic Specification integer division: (x/y).truncate
|
1617
|
+
def divide_int(other, context=nil)
|
1618
|
+
context = Decimal.define_context(context)
|
1619
|
+
other = Decimal._convert(other)
|
1620
|
+
|
1621
|
+
ans = _check_nans(context,other)
|
1622
|
+
return ans if ans
|
1623
|
+
|
1624
|
+
sign = self.sign * other.sign
|
1625
|
+
|
1626
|
+
if self.infinite?
|
1627
|
+
return context.exception(InvalidOperation, 'INF // INF') if other.infinite?
|
1628
|
+
return Decimal.infinity(sign)
|
1629
|
+
end
|
1630
|
+
|
1631
|
+
if other.zero?
|
1632
|
+
if self.zero?
|
1633
|
+
return context.exception(DivisionUndefined, '0 // 0')
|
1634
|
+
else
|
1635
|
+
return context.exception(DivisionByZero, 'x // 0', sign)
|
1636
|
+
end
|
1637
|
+
end
|
1638
|
+
return self._divide_truncate(other, context).first
|
1639
|
+
end
|
1640
|
+
|
1641
|
+
# Ruby-style integer division: (x/y).floor
|
1642
|
+
def div(other, context=nil)
|
1643
|
+
context = Decimal.define_context(context)
|
1644
|
+
other = Decimal._convert(other)
|
1645
|
+
|
1646
|
+
ans = _check_nans(context,other)
|
1647
|
+
return [ans,ans] if ans
|
1648
|
+
|
1649
|
+
sign = self.sign * other.sign
|
1650
|
+
|
1651
|
+
if self.infinite?
|
1652
|
+
return context.exception(InvalidOperation, 'INF // INF') if other.infinite?
|
1653
|
+
return Decimal.infinity(sign)
|
1654
|
+
end
|
1655
|
+
|
1656
|
+
if other.zero?
|
1657
|
+
if self.zero?
|
1658
|
+
return context.exception(DivisionUndefined, '0 // 0')
|
1659
|
+
else
|
1660
|
+
return context.exception(DivisionByZero, 'x // 0', sign)
|
1661
|
+
end
|
1662
|
+
end
|
1663
|
+
return self._divide_floor(other, context).first
|
1664
|
+
end
|
1665
|
+
|
1666
|
+
|
1667
|
+
# Ruby-style modulo: x - y*div(x,y)
|
1668
|
+
def modulo(other, context=nil)
|
1669
|
+
context = Decimal.define_context(context)
|
1670
|
+
other = Decimal._convert(other)
|
1671
|
+
|
1672
|
+
ans = _check_nans(context,other)
|
1673
|
+
return ans if ans
|
1674
|
+
|
1675
|
+
#sign = self.sign * other.sign
|
1676
|
+
|
1677
|
+
if self.infinite?
|
1678
|
+
return context.exception(InvalidOperation, 'INF % x')
|
1679
|
+
elsif other.zero?
|
1680
|
+
if self.zero?
|
1681
|
+
return context.exception(DivisionUndefined, '0 % 0')
|
1682
|
+
else
|
1683
|
+
return context.exception(InvalidOperation, 'x % 0')
|
1684
|
+
end
|
1685
|
+
end
|
1686
|
+
|
1687
|
+
return self._divide_floor(other, context).last._fix(context)
|
1688
|
+
end
|
1689
|
+
|
1690
|
+
# General Decimal Arithmetic Specification remainder: x - y*divide_int(x,y)
|
1691
|
+
def remainder(other, context=nil)
|
1692
|
+
context = Decimal.define_context(context)
|
1693
|
+
other = Decimal._convert(other)
|
1694
|
+
|
1695
|
+
ans = _check_nans(context,other)
|
1696
|
+
return ans if ans
|
1697
|
+
|
1698
|
+
#sign = self.sign * other.sign
|
1699
|
+
|
1700
|
+
if self.infinite?
|
1701
|
+
return context.exception(InvalidOperation, 'INF % x')
|
1702
|
+
elsif other.zero?
|
1703
|
+
if self.zero?
|
1704
|
+
return context.exception(DivisionUndefined, '0 % 0')
|
1705
|
+
else
|
1706
|
+
return context.exception(InvalidOperation, 'x % 0')
|
1707
|
+
end
|
1708
|
+
end
|
1709
|
+
|
1710
|
+
return self._divide_truncate(other, context).last._fix(context)
|
1711
|
+
end
|
1712
|
+
|
1713
|
+
# General Decimal Arithmetic Specification remainder-near:
|
1714
|
+
# x - y*round_half_even(x/y)
|
1715
|
+
def remainder_near(other, context=nil)
|
1716
|
+
context = Decimal.define_context(context)
|
1717
|
+
other = Decimal._convert(other)
|
1718
|
+
|
1719
|
+
ans = _check_nans(context,other)
|
1720
|
+
return ans if ans
|
1721
|
+
|
1722
|
+
sign = self.sign * other.sign
|
1723
|
+
|
1724
|
+
if self.infinite?
|
1725
|
+
return context.exception(InvalidOperation, 'remainder_near(INF,x)')
|
1726
|
+
elsif other.zero?
|
1727
|
+
if self.zero?
|
1728
|
+
return context.exception(DivisionUndefined, 'remainder_near(0,0)')
|
1729
|
+
else
|
1730
|
+
return context.exception(InvalidOperation, 'remainder_near(x,0)')
|
1731
|
+
end
|
1732
|
+
end
|
1733
|
+
|
1734
|
+
if other.infinite?
|
1735
|
+
return Decimal.new(self)._fix(context)
|
1736
|
+
end
|
1737
|
+
|
1738
|
+
ideal_exp = [self.integral_exponent, other.integral_exponent].min
|
1739
|
+
if self.zero?
|
1740
|
+
return Decimal([self.sign, 0, ideal_exp])._fix(context)
|
1741
|
+
end
|
1742
|
+
|
1743
|
+
expdiff = self.adjusted_exponent - other.adjusted_exponent
|
1744
|
+
if (expdiff >= context.precision+1) && !context.exact?
|
1745
|
+
return context.exception(DivisionImpossible)
|
1746
|
+
elsif expdiff <= -2
|
1747
|
+
return self._rescale(ideal_exp, context.rounding)._fix(context)
|
1748
|
+
end
|
1749
|
+
|
1750
|
+
self_coeff = self.integral_significand
|
1751
|
+
other_coeff = other.integral_significand
|
1752
|
+
de = self.integral_exponent - other.integral_exponent
|
1753
|
+
if de >= 0
|
1754
|
+
self_coeff = Decimal.int_mult_radix_power(self_coeff, de)
|
1755
|
+
else
|
1756
|
+
other_coeff = Decimal.int_mult_radix_power(other_coeff, -de)
|
1757
|
+
end
|
1758
|
+
q, r = self_coeff.divmod(other_coeff)
|
1759
|
+
if 2*r + (q&1) > other_coeff
|
1760
|
+
r -= other_coeff
|
1761
|
+
q += 1
|
1762
|
+
end
|
1763
|
+
|
1764
|
+
return context.exception(DivisionImpossible) if q >= Decimal.int_radix_power(context.precision) && !context.exact?
|
1765
|
+
|
1766
|
+
sign = self.sign
|
1767
|
+
if r < 0
|
1768
|
+
sign = -sign
|
1769
|
+
r = -r
|
1770
|
+
end
|
1771
|
+
|
1772
|
+
return Decimal.new([sign, r, ideal_exp])._fix(context)
|
1773
|
+
|
1774
|
+
end
|
1775
|
+
|
1776
|
+
# Reduces an operand to its simplest form
|
1777
|
+
# by removing trailing 0s and incrementing the exponent.
|
1778
|
+
# (formerly called normalize in GDAS)
|
1779
|
+
def reduce(context=nil)
|
1780
|
+
context = Decimal.define_context(context)
|
1781
|
+
if special?
|
1782
|
+
ans = _check_nans(context)
|
1783
|
+
return ans if ans
|
1784
|
+
end
|
1785
|
+
dup = _fix(context)
|
1786
|
+
return dup if dup.infinite?
|
1787
|
+
|
1788
|
+
return Decimal.new([dup.sign, 0, 0]) if dup.zero?
|
1789
|
+
|
1790
|
+
exp_max = context.clamp? ? context.etop : context.emax
|
1791
|
+
end_d = nd = dup.number_of_digits
|
1792
|
+
exp = dup.integral_exponent
|
1793
|
+
coeff = dup.integral_significand
|
1794
|
+
dgs = dup.digits
|
1795
|
+
while (dgs[end_d-1]==0) && (exp < exp_max)
|
1796
|
+
exp += 1
|
1797
|
+
end_d -= 1
|
1798
|
+
end
|
1799
|
+
return Decimal.new([dup.sign, coeff/Decimal.int_radix_power(nd-end_d), exp])
|
1800
|
+
|
1801
|
+
end
|
1802
|
+
|
1803
|
+
# Returns the exponent of the magnitude of the most significant digit.
|
1804
|
+
#
|
1805
|
+
# The result is the integer which is the exponent of the magnitude
|
1806
|
+
# of the most significant digit of the number (as though it were truncated
|
1807
|
+
# to a single digit while maintaining the value of that digit and
|
1808
|
+
# without limiting the resulting exponent).
|
1809
|
+
def logb(context=nil)
|
1810
|
+
context = Decimal.define_context(context)
|
1811
|
+
ans = _check_nans(context)
|
1812
|
+
return ans if ans
|
1813
|
+
return Decimal.infinity if infinite?
|
1814
|
+
return context.exception(DivisionByZero,'logb(0)',-1) if zero?
|
1815
|
+
Decimal.new(adjusted_exponent)
|
1816
|
+
end
|
1817
|
+
|
1818
|
+
# Adds a value to the exponent.
|
1819
|
+
def scaleb(other, context=nil)
|
1820
|
+
|
1821
|
+
context = Decimal.define_context(context)
|
1822
|
+
other = Decimal._convert(other)
|
1823
|
+
ans = _check_nans(context, other)
|
1824
|
+
return ans if ans
|
1825
|
+
return context.exception(InvalidOperation) if other.infinite? || other.integral_exponent != 0
|
1826
|
+
unless context.exact?
|
1827
|
+
liminf = -2 * (context.emax + context.precision)
|
1828
|
+
limsup = 2 * (context.emax + context.precision)
|
1829
|
+
i = other.to_i
|
1830
|
+
return context.exception(InvalidOperation) if !((liminf <= i) && (i <= limsup))
|
1831
|
+
end
|
1832
|
+
return Decimal.new(self) if infinite?
|
1833
|
+
return Decimal.new(@sign, @coeff, @exp+i)._fix(context)
|
1834
|
+
|
1835
|
+
end
|
1836
|
+
|
1837
|
+
# Convert to other numerical type.
|
1838
|
+
def convert_to(type, context=nil)
|
1839
|
+
context = Decimal.define_context(context)
|
1840
|
+
context.convert_to(type, self)
|
1841
|
+
end
|
1842
|
+
|
1843
|
+
# Ruby-style to integer conversion.
|
1844
|
+
def to_i
|
1845
|
+
if special?
|
1846
|
+
return nil if nan?
|
1847
|
+
raise Error, "Cannot convert infinity to Integer"
|
1848
|
+
end
|
1849
|
+
if @exp >= 0
|
1850
|
+
return @sign*Decimal.int_mult_radix_power(@coeff,@exp)
|
1851
|
+
else
|
1852
|
+
return @sign*Decimal.int_div_radix_power(@coeff,-@exp)
|
1853
|
+
end
|
1854
|
+
end
|
1855
|
+
|
1856
|
+
# Ruby-style to string conversion.
|
1857
|
+
def to_s(eng=false,context=nil)
|
1858
|
+
# (context || Decimal.context).to_string(self)
|
1859
|
+
context = Decimal.define_context(context)
|
1860
|
+
sgn = sign<0 ? '-' : ''
|
1861
|
+
if special?
|
1862
|
+
if @exp==:inf
|
1863
|
+
"#{sgn}Infinity"
|
1864
|
+
elsif @exp==:nan
|
1865
|
+
"#{sgn}NaN#{@coeff}"
|
1866
|
+
else # exp==:snan
|
1867
|
+
"#{sgn}sNaN#{@coeff}"
|
1868
|
+
end
|
1869
|
+
else
|
1870
|
+
ds = @coeff.to_s
|
1871
|
+
n_ds = ds.size
|
1872
|
+
exp = integral_exponent
|
1873
|
+
leftdigits = exp + n_ds
|
1874
|
+
if exp<=0 && leftdigits>-6
|
1875
|
+
dotplace = leftdigits
|
1876
|
+
elsif !eng
|
1877
|
+
dotplace = 1
|
1878
|
+
elsif @coeff==0
|
1879
|
+
dotplace = (leftdigits+1)%3 - 1
|
1880
|
+
else
|
1881
|
+
dotplace = (leftdigits-1)%3 + 1
|
1882
|
+
end
|
1883
|
+
|
1884
|
+
if dotplace <=0
|
1885
|
+
intpart = '0'
|
1886
|
+
fracpart = '.' + '0'*(-dotplace) + ds
|
1887
|
+
elsif dotplace >= n_ds
|
1888
|
+
intpart = ds + '0'*(dotplace - n_ds)
|
1889
|
+
fracpart = ''
|
1890
|
+
else
|
1891
|
+
intpart = ds[0...dotplace]
|
1892
|
+
fracpart = '.' + ds[dotplace..-1]
|
1893
|
+
end
|
1894
|
+
|
1895
|
+
if leftdigits == dotplace
|
1896
|
+
e = ''
|
1897
|
+
else
|
1898
|
+
e = (context.capitals ? 'E' : 'e') + "%+d"%(leftdigits-dotplace)
|
1899
|
+
end
|
1900
|
+
|
1901
|
+
sgn + intpart + fracpart + e
|
1902
|
+
|
1903
|
+
end
|
1904
|
+
end
|
1905
|
+
|
1906
|
+
# Conversion to Rational.
|
1907
|
+
# Conversion of special values will raise an exception under Ruby 1.9
|
1908
|
+
def to_r
|
1909
|
+
if special?
|
1910
|
+
num = (@exp == :inf) ? @sign : 0
|
1911
|
+
Rational.respond_to?(:new!) ? Rational.new!(num,0) : Rational(num,0)
|
1912
|
+
else
|
1913
|
+
if @exp < 0
|
1914
|
+
Rational(@sign*@coeff, Decimal.int_radix_power(-@exp))
|
1915
|
+
else
|
1916
|
+
Rational(Decimal.int_mult_radix_power(@sign*@coeff,@exp), 1)
|
1917
|
+
end
|
1918
|
+
end
|
1919
|
+
end
|
1920
|
+
|
1921
|
+
# Conversion to Float
|
1922
|
+
def to_f
|
1923
|
+
if special?
|
1924
|
+
if @exp==:inf
|
1925
|
+
@sign/0.0
|
1926
|
+
else
|
1927
|
+
0.0/0.0
|
1928
|
+
end
|
1929
|
+
else
|
1930
|
+
# to_rational.to_f
|
1931
|
+
# to_s.to_f
|
1932
|
+
@sign*@coeff*(10.0**@exp)
|
1933
|
+
end
|
1934
|
+
end
|
1935
|
+
|
1936
|
+
def inspect
|
1937
|
+
#"Decimal('#{self}')"
|
1938
|
+
#debug:
|
1939
|
+
"Decimal('#{self}') [coeff:#{@coeff.inspect} exp:#{@exp.inspect} s:#{@sign.inspect}]"
|
1940
|
+
end
|
1941
|
+
|
1942
|
+
# Internal comparison operator: returns -1 if the first number is less than the second,
|
1943
|
+
# 0 if both are equal or +1 if the first is greater than the secong.
|
1944
|
+
def <=>(other)
|
1945
|
+
case other
|
1946
|
+
when *Decimal.context.coercible_types_or_decimal
|
1947
|
+
other = Decimal(other)
|
1948
|
+
if self.special? || other.special?
|
1949
|
+
if self.nan? || other.nan?
|
1950
|
+
1
|
1951
|
+
else
|
1952
|
+
self_v = self.finite? ? 0 : self.sign
|
1953
|
+
other_v = other.finite? ? 0 : other.sign
|
1954
|
+
self_v <=> other_v
|
1955
|
+
end
|
1956
|
+
else
|
1957
|
+
if self.zero?
|
1958
|
+
if other.zero?
|
1959
|
+
0
|
1960
|
+
else
|
1961
|
+
-other.sign
|
1962
|
+
end
|
1963
|
+
elsif other.zero?
|
1964
|
+
self.sign
|
1965
|
+
elsif other.sign < self.sign
|
1966
|
+
+1
|
1967
|
+
elsif self.sign < other.sign
|
1968
|
+
-1
|
1969
|
+
else
|
1970
|
+
self_adjusted = self.adjusted_exponent
|
1971
|
+
other_adjusted = other.adjusted_exponent
|
1972
|
+
if self_adjusted == other_adjusted
|
1973
|
+
self_padded,other_padded = self.integral_significand,other.integral_significand
|
1974
|
+
d = self.integral_exponent - other.integral_exponent
|
1975
|
+
if d>0
|
1976
|
+
self_padded *= Decimal.int_radix_power(d)
|
1977
|
+
else
|
1978
|
+
other_padded *= Decimal.int_radix_power(-d)
|
1979
|
+
end
|
1980
|
+
(self_padded <=> other_padded)*self.sign
|
1981
|
+
elsif self_adjusted > other_adjusted
|
1982
|
+
self.sign
|
1983
|
+
else
|
1984
|
+
-self.sign
|
1985
|
+
end
|
1986
|
+
end
|
1987
|
+
end
|
1988
|
+
else
|
1989
|
+
if defined? other.coerce
|
1990
|
+
x, y = other.coerce(self)
|
1991
|
+
x <=> y
|
1992
|
+
else
|
1993
|
+
nil
|
1994
|
+
end
|
1995
|
+
end
|
1996
|
+
end
|
1997
|
+
def ==(other)
|
1998
|
+
(self<=>other) == 0
|
1999
|
+
end
|
2000
|
+
include Comparable
|
2001
|
+
|
2002
|
+
def hash
|
2003
|
+
([Decimal]+reduce.split).hash # TODO: optimize
|
2004
|
+
end
|
2005
|
+
|
2006
|
+
def eql?(other)
|
2007
|
+
return false unless other.is_a?(Decimal)
|
2008
|
+
reduce.split == other.reduce.split
|
2009
|
+
end
|
2010
|
+
|
2011
|
+
# Compares like <=> but returns a Decimal value.
|
2012
|
+
def compare(other, context=nil)
|
2013
|
+
|
2014
|
+
other = Decimal._convert(other)
|
2015
|
+
|
2016
|
+
if self.special? || other.special?
|
2017
|
+
ans = _check_nans(context, other)
|
2018
|
+
return ans if ans
|
2019
|
+
end
|
2020
|
+
|
2021
|
+
return Decimal(self <=> other)
|
2022
|
+
|
2023
|
+
end
|
2024
|
+
|
2025
|
+
# Digits of the significand as an array of integers
|
2026
|
+
def digits
|
2027
|
+
@coeff.to_s.split('').map{|d| d.to_i}
|
2028
|
+
end
|
2029
|
+
|
2030
|
+
# Exponent of the magnitude of the most significant digit of the operand
|
2031
|
+
def adjusted_exponent
|
2032
|
+
if special?
|
2033
|
+
0
|
2034
|
+
else
|
2035
|
+
@exp + number_of_digits - 1
|
2036
|
+
end
|
2037
|
+
end
|
2038
|
+
|
2039
|
+
# Synonym for Decimal#adjusted_exponent()
|
2040
|
+
def scientific_exponent
|
2041
|
+
adjusted_exponent
|
2042
|
+
end
|
2043
|
+
|
2044
|
+
# Exponent as though the significand were a fraction (the decimal point before its first digit)
|
2045
|
+
def fractional_exponent
|
2046
|
+
scientific_exponent + 1
|
2047
|
+
end
|
2048
|
+
|
2049
|
+
# Number of digits in the significand
|
2050
|
+
def number_of_digits
|
2051
|
+
# digits.size
|
2052
|
+
@coeff.to_s.size
|
2053
|
+
end
|
2054
|
+
|
2055
|
+
# Significand as an integer
|
2056
|
+
def integral_significand
|
2057
|
+
@coeff
|
2058
|
+
end
|
2059
|
+
|
2060
|
+
# Exponent of the significand as an integer
|
2061
|
+
def integral_exponent
|
2062
|
+
fractional_exponent - number_of_digits
|
2063
|
+
end
|
2064
|
+
|
2065
|
+
# Sign of the number: +1 for plus / -1 for minus.
|
2066
|
+
def sign
|
2067
|
+
@sign
|
2068
|
+
end
|
2069
|
+
|
2070
|
+
# Return the value of the number as an integer and a scale.
|
2071
|
+
def to_int_scale
|
2072
|
+
if special?
|
2073
|
+
nil
|
2074
|
+
else
|
2075
|
+
[@sign*integral_significand, integral_exponent]
|
2076
|
+
end
|
2077
|
+
end
|
2078
|
+
|
2079
|
+
# Returns copy with sign inverted
|
2080
|
+
def _neg(context=nil)
|
2081
|
+
if special?
|
2082
|
+
ans = _check_nans(context)
|
2083
|
+
return ans if ans
|
2084
|
+
end
|
2085
|
+
if zero?
|
2086
|
+
ans = copy_abs
|
2087
|
+
else
|
2088
|
+
ans = copy_negate
|
2089
|
+
end
|
2090
|
+
context = Decimal.define_context(context)
|
2091
|
+
ans._fix(context)
|
2092
|
+
end
|
2093
|
+
|
2094
|
+
# Returns a copy with precision adjusted
|
2095
|
+
def _pos(context=nil)
|
2096
|
+
if special?
|
2097
|
+
ans = _check_nans(context)
|
2098
|
+
return ans if ans
|
2099
|
+
end
|
2100
|
+
if zero?
|
2101
|
+
ans = copy_abs
|
2102
|
+
else
|
2103
|
+
ans = Decimal.new(self)
|
2104
|
+
end
|
2105
|
+
context = Decimal.define_context(context)
|
2106
|
+
ans._fix(context)
|
2107
|
+
end
|
2108
|
+
|
2109
|
+
# Returns a copy with positive sign
|
2110
|
+
def _abs(round=true, context=nil)
|
2111
|
+
return copy_abs if not round
|
2112
|
+
|
2113
|
+
if special?
|
2114
|
+
ans = _check_nans(context)
|
2115
|
+
return ans if ans
|
2116
|
+
end
|
2117
|
+
if sign>0
|
2118
|
+
ans = _neg(context)
|
2119
|
+
else
|
2120
|
+
ans = _pos(context)
|
2121
|
+
end
|
2122
|
+
ans
|
2123
|
+
end
|
2124
|
+
|
2125
|
+
# Round if it is necessary to keep within precision.
|
2126
|
+
def _fix(context)
|
2127
|
+
return self if context.exact?
|
2128
|
+
|
2129
|
+
if special?
|
2130
|
+
if nan?
|
2131
|
+
return _fix_nan(context)
|
2132
|
+
else
|
2133
|
+
return Decimal.new(self)
|
2134
|
+
end
|
2135
|
+
end
|
2136
|
+
|
2137
|
+
etiny = context.etiny
|
2138
|
+
etop = context.etop
|
2139
|
+
if zero?
|
2140
|
+
exp_max = context.clamp? ? etop : context.emax
|
2141
|
+
new_exp = [[@exp, etiny].max, exp_max].min
|
2142
|
+
if new_exp!=@exp
|
2143
|
+
context.exception Clamped
|
2144
|
+
return Decimal.new([sign,0,new_exp])
|
2145
|
+
else
|
2146
|
+
return Decimal.new(self)
|
2147
|
+
end
|
2148
|
+
end
|
2149
|
+
|
2150
|
+
nd = number_of_digits
|
2151
|
+
exp_min = nd + @exp - context.precision
|
2152
|
+
if exp_min > etop
|
2153
|
+
context.exception Inexact
|
2154
|
+
context.exception Rounded
|
2155
|
+
return context.exception(Overflow, 'above Emax', sign)
|
2156
|
+
end
|
2157
|
+
|
2158
|
+
self_is_subnormal = exp_min < etiny
|
2159
|
+
|
2160
|
+
if self_is_subnormal
|
2161
|
+
context.exception Subnormal
|
2162
|
+
exp_min = etiny
|
2163
|
+
end
|
2164
|
+
|
2165
|
+
if @exp < exp_min
|
2166
|
+
#puts "_fix(#{self}) rounded; e=#{@exp} em=#{exp_min}"
|
2167
|
+
context.exception Rounded
|
2168
|
+
# dig is the digits number from 0 (MS) to number_of_digits-1 (LS)
|
2169
|
+
# dg = numberof_digits-dig is from 1 (LS) to number_of_digits (MS)
|
2170
|
+
dg = exp_min - @exp # dig = number_of_digits + exp - exp_min
|
2171
|
+
if dg > number_of_digits # dig<0
|
2172
|
+
d = Decimal.new([sign,1,exp_min-1])
|
2173
|
+
dg = number_of_digits # dig = 0
|
2174
|
+
else
|
2175
|
+
d = Decimal.new(self)
|
2176
|
+
end
|
2177
|
+
changed = d._round(context.rounding, dg)
|
2178
|
+
coeff = Decimal.int_div_radix_power(d.integral_significand, dg)
|
2179
|
+
coeff += 1 if changed==1
|
2180
|
+
ans = Decimal.new([sign, coeff, exp_min])
|
2181
|
+
if changed!=0
|
2182
|
+
context.exception Inexact
|
2183
|
+
if self_is_subnormal
|
2184
|
+
context.exception Underflow
|
2185
|
+
if ans.zero?
|
2186
|
+
context.exception Clamped
|
2187
|
+
end
|
2188
|
+
elsif ans.number_of_digits == context.precision+1
|
2189
|
+
if ans.integral_exponent< etop
|
2190
|
+
ans = Decimal.new([ans.sign, Decimal.int_div_radix_power(ans.integral_significand,1), ans.integral_exponent+1])
|
2191
|
+
else
|
2192
|
+
ans = context.exception(Overflow, 'above Emax', d.sign)
|
2193
|
+
end
|
2194
|
+
end
|
2195
|
+
end
|
2196
|
+
return ans
|
2197
|
+
end
|
2198
|
+
|
2199
|
+
if context.clamp? && @exp>etop
|
2200
|
+
context.exception Clamped
|
2201
|
+
self_padded = Decimal.int_mult_radix_power(@coeff, @exp-etop)
|
2202
|
+
return Decimal.new([sign,self_padded,etop])
|
2203
|
+
end
|
2204
|
+
|
2205
|
+
return Decimal.new(self)
|
2206
|
+
|
2207
|
+
end
|
2208
|
+
|
2209
|
+
|
2210
|
+
ROUND_ARITHMETIC = true
|
2211
|
+
|
2212
|
+
# Round to i digits using the specified method
|
2213
|
+
def _round(rounding, i)
|
2214
|
+
send("_round_#{rounding}", i)
|
2215
|
+
end
|
2216
|
+
|
2217
|
+
# Round down (toward 0, truncate) to i digits
|
2218
|
+
def _round_down(i)
|
2219
|
+
if ROUND_ARITHMETIC
|
2220
|
+
(@coeff % Decimal.int_radix_power(i))==0 ? 0 : -1
|
2221
|
+
else
|
2222
|
+
d = @coeff.to_s
|
2223
|
+
p = d.size - i
|
2224
|
+
d[p..-1].match(/\A0+\Z/) ? 0 : -1
|
2225
|
+
end
|
2226
|
+
end
|
2227
|
+
|
2228
|
+
# Round up (away from 0) to i digits
|
2229
|
+
def _round_up(i)
|
2230
|
+
-_round_down(i)
|
2231
|
+
end
|
2232
|
+
|
2233
|
+
# Round to closest i-digit number with ties down (rounds 5 toward 0)
|
2234
|
+
def _round_half_down(i)
|
2235
|
+
if ROUND_ARITHMETIC
|
2236
|
+
m = Decimal.int_radix_power(i)
|
2237
|
+
if (m>1) && ((@coeff%m) == m/2)
|
2238
|
+
-1
|
2239
|
+
else
|
2240
|
+
_round_half_up(i)
|
2241
|
+
end
|
2242
|
+
else
|
2243
|
+
d = @coeff.to_s
|
2244
|
+
p = d.size - i
|
2245
|
+
d[p..-1].match(/^5d*$/) ? -1 : _round_half_up(i)
|
2246
|
+
end
|
2247
|
+
|
2248
|
+
end
|
2249
|
+
|
2250
|
+
# Round to closest i-digit number with ties up (rounds 5 away from 0)
|
2251
|
+
def _round_half_up(i)
|
2252
|
+
if ROUND_ARITHMETIC
|
2253
|
+
m = Decimal.int_radix_power(i)
|
2254
|
+
if (m>1) && ((@coeff%m) >= m/2)
|
2255
|
+
1
|
2256
|
+
else
|
2257
|
+
(@coeff % m)==0 ? 0 : -1
|
2258
|
+
end
|
2259
|
+
else
|
2260
|
+
d = @coeff.to_s
|
2261
|
+
p = d.size - i
|
2262
|
+
if '56789'.include?(d[p,1])
|
2263
|
+
1
|
2264
|
+
else
|
2265
|
+
d[p..-1].match(/^0+$/) ? 0 : -1
|
2266
|
+
end
|
2267
|
+
end
|
2268
|
+
|
2269
|
+
end
|
2270
|
+
|
2271
|
+
# Round to closest i-digit number with ties (5) to an even digit
|
2272
|
+
def _round_half_even(i)
|
2273
|
+
if ROUND_ARITHMETIC
|
2274
|
+
m = Decimal.int_radix_power(i)
|
2275
|
+
if (m>1) && ((@coeff%m) == m/2 && ((@coeff/m)%2)==0)
|
2276
|
+
-1
|
2277
|
+
else
|
2278
|
+
_round_half_up(i)
|
2279
|
+
end
|
2280
|
+
else
|
2281
|
+
d = @coeff.to_s
|
2282
|
+
p = d.size - i
|
2283
|
+
|
2284
|
+
if d[p..-1].match(/\A#{radix/2}0*\Z/) && (p==0 || ((d[p-1,1].to_i%2)==0))
|
2285
|
+
-1
|
2286
|
+
else
|
2287
|
+
_round_half_up(i)
|
2288
|
+
end
|
2289
|
+
|
2290
|
+
end
|
2291
|
+
end
|
2292
|
+
|
2293
|
+
# Round up (not away from 0 if negative) to i digits
|
2294
|
+
def _round_ceiling(i)
|
2295
|
+
sign<0 ? _round_down(i) : -_round_down(i)
|
2296
|
+
end
|
2297
|
+
|
2298
|
+
# Round down (not toward 0 if negative) to i digits
|
2299
|
+
def _round_floor(i)
|
2300
|
+
sign>0 ? _round_down(i) : -_round_down(i)
|
2301
|
+
end
|
2302
|
+
|
2303
|
+
# Round down unless digit i-1 is 0 or 5
|
2304
|
+
def _round_up05(i)
|
2305
|
+
if ROUND_ARITHMETIC
|
2306
|
+
dg = (@coeff%Decimal.int_radix_power(i+1))/Decimal.int_radix_power(i)
|
2307
|
+
else
|
2308
|
+
d = @coeff.to_s
|
2309
|
+
p = d.size - i
|
2310
|
+
dg = (p>0) ? d[p-1,1].to_i : 0
|
2311
|
+
end
|
2312
|
+
if [0,Decimal.radix/2].include?(dg)
|
2313
|
+
-_round_down(i)
|
2314
|
+
else
|
2315
|
+
_round_down(i)
|
2316
|
+
end
|
2317
|
+
end
|
2318
|
+
|
2319
|
+
# adjust payload of a NaN to the context
|
2320
|
+
def _fix_nan(context)
|
2321
|
+
if !context.exact?
|
2322
|
+
payload = @coeff
|
2323
|
+
payload = nil if payload==0
|
2324
|
+
|
2325
|
+
max_payload_len = context.maximum_nan_diagnostic_digits
|
2326
|
+
|
2327
|
+
if number_of_digits > max_payload_len
|
2328
|
+
payload = payload.to_s[-max_payload_len..-1].to_i
|
2329
|
+
return Decimal([@sign, payload, @exp])
|
2330
|
+
end
|
2331
|
+
end
|
2332
|
+
Decimal(self)
|
2333
|
+
end
|
2334
|
+
|
2335
|
+
# Check if the number or other is NaN, signal if sNaN or return NaN;
|
2336
|
+
# return nil if none is NaN.
|
2337
|
+
def _check_nans(context=nil, other=nil)
|
2338
|
+
#self_is_nan = self.nan?
|
2339
|
+
#other_is_nan = other.nil? ? false : other.nan?
|
2340
|
+
if self.nan? || (other && other.nan?)
|
2341
|
+
context = Decimal.define_context(context)
|
2342
|
+
return context.exception(InvalidOperation, 'sNaN', self) if self.snan?
|
2343
|
+
return context.exception(InvalidOperation, 'sNaN', other) if other && other.snan?
|
2344
|
+
return self._fix_nan(context) if self.nan?
|
2345
|
+
return other._fix_nan(context)
|
2346
|
+
else
|
2347
|
+
return nil
|
2348
|
+
end
|
2349
|
+
end
|
2350
|
+
|
2351
|
+
# Rescale so that the exponent is exp, either by padding with zeros
|
2352
|
+
# or by truncating digits, using the given rounding mode.
|
2353
|
+
#
|
2354
|
+
# Specials are returned without change. This operation is
|
2355
|
+
# quiet: it raises no flags, and uses no information from the
|
2356
|
+
# context.
|
2357
|
+
#
|
2358
|
+
# exp = exp to scale to (an integer)
|
2359
|
+
# rounding = rounding mode
|
2360
|
+
def _rescale(exp, rounding)
|
2361
|
+
|
2362
|
+
return Decimal.new(self) if special?
|
2363
|
+
return Decimal.new([sign, 0, exp]) if zero?
|
2364
|
+
return Decimal.new([sign, @coeff*Decimal.int_radix_power(self.integral_exponent - exp), exp]) if self.integral_exponent > exp
|
2365
|
+
#nd = number_of_digits + self.integral_exponent - exp
|
2366
|
+
nd = exp - self.integral_exponent
|
2367
|
+
if number_of_digits < nd
|
2368
|
+
slf = Decimal.new([sign, 1, exp-1])
|
2369
|
+
nd = number_of_digits
|
2370
|
+
else
|
2371
|
+
slf = Decimal.new(self)
|
2372
|
+
end
|
2373
|
+
changed = slf._round(rounding, nd)
|
2374
|
+
coeff = Decimal.int_div_radix_power(@coeff, nd)
|
2375
|
+
coeff += 1 if changed==1
|
2376
|
+
Decimal.new([slf.sign, coeff, exp])
|
2377
|
+
|
2378
|
+
end
|
2379
|
+
|
2380
|
+
# Normalizes op1, op2 to have the same exp and length of coefficient. Used for addition.
|
2381
|
+
def Decimal._normalize(op1, op2, prec=0)
|
2382
|
+
#puts "N: #{op1.inspect} #{op2.inspect} p=#{prec}"
|
2383
|
+
if op1.integral_exponent < op2.integral_exponent
|
2384
|
+
swap = true
|
2385
|
+
tmp,other = op2,op1
|
2386
|
+
else
|
2387
|
+
swap = false
|
2388
|
+
tmp,other = op1,op2
|
2389
|
+
end
|
2390
|
+
tmp_len = tmp.number_of_digits
|
2391
|
+
other_len = other.number_of_digits
|
2392
|
+
exp = tmp.integral_exponent + [-1, tmp_len - prec - 2].min
|
2393
|
+
#puts "exp=#{exp}"
|
2394
|
+
if (other_len+other.integral_exponent-1 < exp) && prec>0
|
2395
|
+
other = Decimal.new([other.sign, 1, exp])
|
2396
|
+
#puts "other = #{other.inspect}"
|
2397
|
+
end
|
2398
|
+
tmp = Decimal.new([tmp.sign, int_mult_radix_power(tmp.integral_significand, tmp.integral_exponent-other.integral_exponent), other.integral_exponent])
|
2399
|
+
#puts "tmp=#{tmp.inspect}"
|
2400
|
+
return swap ? [other, tmp] : [tmp, other]
|
2401
|
+
end
|
2402
|
+
|
2403
|
+
# Returns a copy of with the sign set to +
|
2404
|
+
def copy_abs
|
2405
|
+
Decimal.new([+1,@coeff,@exp])
|
2406
|
+
end
|
2407
|
+
|
2408
|
+
# Returns a copy of with the sign inverted
|
2409
|
+
def copy_negate
|
2410
|
+
Decimal.new([-@sign,@coeff,@exp])
|
2411
|
+
end
|
2412
|
+
|
2413
|
+
# Returns a copy of with the sign of other
|
2414
|
+
def copy_sign(other)
|
2415
|
+
Decimal.new([other.sign, @coeff, @exp])
|
2416
|
+
end
|
2417
|
+
|
2418
|
+
# Returns true if the value is an integer
|
2419
|
+
def integral?
|
2420
|
+
if finite?
|
2421
|
+
if @exp>=0 || @coeff==0
|
2422
|
+
true
|
2423
|
+
else
|
2424
|
+
if @exp <= -number_of_digits
|
2425
|
+
false
|
2426
|
+
else
|
2427
|
+
m = Decimal.int_radix_power(-@exp)
|
2428
|
+
(@coeff % m) == 0
|
2429
|
+
end
|
2430
|
+
end
|
2431
|
+
else
|
2432
|
+
false
|
2433
|
+
end
|
2434
|
+
end
|
2435
|
+
|
2436
|
+
# Rescale so that the exponent is exp, either by padding with zeros
|
2437
|
+
# or by truncating digits.
|
2438
|
+
def rescale(exp, context=nil, watch_exp=true)
|
2439
|
+
context = Decimal.define_context(context)
|
2440
|
+
exp = Decimal._convert(exp)
|
2441
|
+
if self.special? || exp.special?
|
2442
|
+
ans = _check_nans(context, exp)
|
2443
|
+
return ans if ans
|
2444
|
+
if exp.infinite? || self.infinite?
|
2445
|
+
return Decimal.new(self) if exp.infinite? && self.infinite?
|
2446
|
+
return context.exception(InvalidOperation, 'rescale with one INF')
|
2447
|
+
end
|
2448
|
+
end
|
2449
|
+
return context.exception(InvalidOperation,"exponent of rescale is not integral") unless exp.integral?
|
2450
|
+
exp = exp.to_i
|
2451
|
+
_watched_rescale(exp, context, watch_exp)
|
2452
|
+
end
|
2453
|
+
|
2454
|
+
# Quantize so its exponent is the same as that of y.
|
2455
|
+
def quantize(exp, context=nil, watch_exp=true)
|
2456
|
+
exp = Decimal._convert(exp)
|
2457
|
+
context = Decimal.define_context(context)
|
2458
|
+
if self.special? || exp.special?
|
2459
|
+
ans = _check_nans(context, exp)
|
2460
|
+
return ans if ans
|
2461
|
+
if exp.infinite? || self.infinite?
|
2462
|
+
return Decimal.new(self) if exp.infinite? && self.infinite?
|
2463
|
+
return context.exception(InvalidOperation, 'quantize with one INF')
|
2464
|
+
end
|
2465
|
+
end
|
2466
|
+
exp = exp.integral_exponent
|
2467
|
+
_watched_rescale(exp, context, watch_exp)
|
2468
|
+
end
|
2469
|
+
|
2470
|
+
def _watched_rescale(exp, context, watch_exp)
|
2471
|
+
if !watch_exp
|
2472
|
+
ans = _rescale(exp, context.rounding)
|
2473
|
+
context.exception(Rounded) if ans.integral_exponent > self.integral_exponent
|
2474
|
+
context.exception(Inexact) if ans != self
|
2475
|
+
return ans
|
2476
|
+
end
|
2477
|
+
|
2478
|
+
if exp < context.etiny || exp > context.emax
|
2479
|
+
return context.exception(InvalidOperation, "target operation out of bounds in quantize/rescale")
|
2480
|
+
end
|
2481
|
+
|
2482
|
+
return Decimal.new([@sign, 0, exp])._fix(context) if zero?
|
2483
|
+
|
2484
|
+
self_adjusted = adjusted_exponent
|
2485
|
+
return context.exception(InvalidOperation,"exponent of quantize/rescale result too large for current context") if self_adjusted > context.emax
|
2486
|
+
return context.exception(InvalidOperation,"quantize/rescale has too many digits for current context") if (self_adjusted - exp + 1 > context.precision) && !context.exact?
|
2487
|
+
|
2488
|
+
ans = _rescale(exp, context.rounding)
|
2489
|
+
return context.exception(InvalidOperation,"exponent of rescale result too large for current context") if ans.adjusted_exponent > context.emax
|
2490
|
+
return context.exception(InvalidOperation,"rescale result has too many digits for current context") if (ans.number_of_digits > context.precision) && !context.exact?
|
2491
|
+
if ans.integral_exponent > self.integral_exponent
|
2492
|
+
context.exception(Rounded)
|
2493
|
+
context.exception(Inexact) if ans!=self
|
2494
|
+
end
|
2495
|
+
context.exception(Subnormal) if !ans.zero? && (ans.adjusted_exponent < context.emin)
|
2496
|
+
return ans._fix(context)
|
2497
|
+
end
|
2498
|
+
|
2499
|
+
# Return true if has the same exponent as other.
|
2500
|
+
#
|
2501
|
+
# If either operand is a special value, the following rules are used:
|
2502
|
+
# * return true if both operands are infinities
|
2503
|
+
# * return true if both operands are NaNs
|
2504
|
+
# * otherwise, return false.
|
2505
|
+
def same_quantum?(other)
|
2506
|
+
other = Decimal._convert(other)
|
2507
|
+
if self.special? || other.special?
|
2508
|
+
return (self.nan? && other.nan?) || (self.infinite? && other.infinite?)
|
2509
|
+
end
|
2510
|
+
return self.integral_exponent == other.integral_exponent
|
2511
|
+
end
|
2512
|
+
|
2513
|
+
# Rounds to a nearby integer. May raise Inexact or Rounded.
|
2514
|
+
def to_integral_exact(context=nil)
|
2515
|
+
context = Decimal.define_context(context)
|
2516
|
+
if special?
|
2517
|
+
ans = _check_nans(context)
|
2518
|
+
return ans if ans
|
2519
|
+
return Decimal.new(self)
|
2520
|
+
end
|
2521
|
+
return Decimal.new(self) if @exp >= 0
|
2522
|
+
return Decimal.new([@sign, 0, 0]) if zero?
|
2523
|
+
context.exception Rounded
|
2524
|
+
ans = _rescale(0, context.rounding)
|
2525
|
+
context.exception Inexact if ans != self
|
2526
|
+
return ans
|
2527
|
+
end
|
2528
|
+
|
2529
|
+
# Rounds to a nearby integer. Doesn't raise Inexact or Rounded.
|
2530
|
+
def to_integral_value(context=nil)
|
2531
|
+
context = Decimal.define_context(context)
|
2532
|
+
if special?
|
2533
|
+
ans = _check_nans(context)
|
2534
|
+
return ans if ans
|
2535
|
+
return Decimal.new(self)
|
2536
|
+
end
|
2537
|
+
return Decimal.new(self) if @exp >= 0
|
2538
|
+
return _rescale(0, context.rounding)
|
2539
|
+
end
|
2540
|
+
|
2541
|
+
# General rounding.
|
2542
|
+
#
|
2543
|
+
# With an integer argument this acts like Float#round: the parameter specifies the number
|
2544
|
+
# of fractional digits (or digits to the left of the decimal point if negative).
|
2545
|
+
#
|
2546
|
+
# Options can be passed as a Hash instead; valid options are:
|
2547
|
+
# * :rounding method for rounding (see Context#new())
|
2548
|
+
# The precision can be specified as:
|
2549
|
+
# * :places number of fractional digits as above.
|
2550
|
+
# * :exponent specifies the exponent corresponding to the
|
2551
|
+
# digit to be rounded (exponent == -places)
|
2552
|
+
# * :precision or :significan_digits is the number of digits
|
2553
|
+
# * :power 10^exponent, value of the digit to be rounded,
|
2554
|
+
# should be passed as a type convertible to Decimal.
|
2555
|
+
# * :index 0-based index of the digit to be rounded
|
2556
|
+
# * :rindex right 0-based index of the digit to be rounded
|
2557
|
+
#
|
2558
|
+
# The default is :places=>0 (round to integer).
|
2559
|
+
#
|
2560
|
+
# Example: ways of specifiying the rounding position
|
2561
|
+
# number: 1 2 3 4 . 5 6 7 8
|
2562
|
+
# :places -3 -2 -1 0 1 2 3 4
|
2563
|
+
# :exponent 3 2 1 0 -1 -2 -3 -4
|
2564
|
+
# :precision 1 2 3 4 5 6 7 8
|
2565
|
+
# :power 1E3 1E2 10 1 0.1 1E-2 1E-3 1E-4
|
2566
|
+
# :index 0 1 2 3 4 5 6 7
|
2567
|
+
# :index 7 6 5 4 3 2 1 0
|
2568
|
+
def round(opt={})
|
2569
|
+
opt = { :places=>opt } if opt.kind_of?(Integer)
|
2570
|
+
r = opt[:rounding] || :half_up
|
2571
|
+
as_int = false
|
2572
|
+
if v=(opt[:precision] || opt[:significant_digits])
|
2573
|
+
prec = v
|
2574
|
+
elsif v=(opt[:places])
|
2575
|
+
prec = adjusted_exponent + 1 + v
|
2576
|
+
elsif v=(opt[:exponent])
|
2577
|
+
prec = adjusted_exponent + 1 - v
|
2578
|
+
elsif v=(opt[:power])
|
2579
|
+
prec = adjusted_exponent + 1 - Decimal(v).adjusted_exponent
|
2580
|
+
elsif v=(opt[:index])
|
2581
|
+
prec = i+1
|
2582
|
+
elsif v=(opt[:rindex])
|
2583
|
+
prec = number_of_digits - v
|
2584
|
+
else
|
2585
|
+
prec = adjusted_exponent + 1
|
2586
|
+
as_int = true
|
2587
|
+
end
|
2588
|
+
result = plus(:rounding=>r, :precision=>prec)
|
2589
|
+
return as_int ? result.to_i : result
|
2590
|
+
end
|
2591
|
+
|
2592
|
+
# General ceiling operation (as for Float) with same options for precision
|
2593
|
+
# as Decimal#round()
|
2594
|
+
def ceil(opt={})
|
2595
|
+
opt[:rounding] = :ceiling
|
2596
|
+
round opt
|
2597
|
+
end
|
2598
|
+
|
2599
|
+
# General floor operation (as for Float) with same options for precision
|
2600
|
+
# as Decimal#round()
|
2601
|
+
def floor(opt={})
|
2602
|
+
opt[:rounding] = :floor
|
2603
|
+
round opt
|
2604
|
+
end
|
2605
|
+
|
2606
|
+
# General truncate operation (as for Float) with same options for precision
|
2607
|
+
# as Decimal#round()
|
2608
|
+
def truncate(opt={})
|
2609
|
+
opt[:rounding] = :down
|
2610
|
+
round opt
|
2611
|
+
end
|
2612
|
+
|
2613
|
+
# Fused multiply-add.
|
2614
|
+
#
|
2615
|
+
# Computes (self*other+third) with no rounding of the intermediate product self*other.
|
2616
|
+
def fma(other, third, context=nil)
|
2617
|
+
context = Decimal.define_context(context)
|
2618
|
+
other = Decimal._convert(other)
|
2619
|
+
third = Decimal._convert(third)
|
2620
|
+
if self.special? || other.special?
|
2621
|
+
return context.exception(InvalidOperation, 'sNaN', self) if self.snan?
|
2622
|
+
return context.exception(InvalidOperation, 'sNaN', other) if other.snan?
|
2623
|
+
if self.nan?
|
2624
|
+
product = self
|
2625
|
+
elsif other.nan?
|
2626
|
+
product = other
|
2627
|
+
elsif self.infinite?
|
2628
|
+
return context.exception(InvalidOperation, 'INF * 0 in fma') if other.zero?
|
2629
|
+
product = Decimal.infinity(self.sign*other.sign)
|
2630
|
+
elsif other.infinite?
|
2631
|
+
return context.exception(InvalidOperation, '0 * INF in fma') if self.zero?
|
2632
|
+
product = Decimal.infinity(self.sign*other.sign)
|
2633
|
+
end
|
2634
|
+
else
|
2635
|
+
product = Decimal.new([self.sign*other.sign,self.integral_significand*other.integral_significand, self.integral_exponent+other.integral_exponent])
|
2636
|
+
end
|
2637
|
+
return product.add(third, context)
|
2638
|
+
end
|
2639
|
+
|
2640
|
+
def _divide_truncate(other, context)
|
2641
|
+
context = Decimal.define_context(context)
|
2642
|
+
sign = self.sign * other.sign
|
2643
|
+
if other.infinite?
|
2644
|
+
ideal_exp = self.integral_exponent
|
2645
|
+
else
|
2646
|
+
ideal_exp = [self.integral_exponent, other.integral_exponent].min
|
2647
|
+
end
|
2648
|
+
|
2649
|
+
expdiff = self.adjusted_exponent - other.adjusted_exponent
|
2650
|
+
if self.zero? || other.infinite? || (expdiff <= -2)
|
2651
|
+
return [Decimal.new([sign, 0, 0]), _rescale(ideal_exp, context.rounding)]
|
2652
|
+
end
|
2653
|
+
if (expdiff <= context.precision) || context.exact?
|
2654
|
+
self_coeff = self.integral_significand
|
2655
|
+
other_coeff = other.integral_significand
|
2656
|
+
de = self.integral_exponent - other.integral_exponent
|
2657
|
+
if de >= 0
|
2658
|
+
self_coeff = Decimal.int_mult_radix_power(self_coeff, de)
|
2659
|
+
else
|
2660
|
+
other_coeff = Decimal.int_mult_radix_power(other_coeff, -de)
|
2661
|
+
end
|
2662
|
+
q, r = self_coeff.divmod(other_coeff)
|
2663
|
+
if (q < Decimal.int_radix_power(context.precision)) || context.exact?
|
2664
|
+
return [Decimal([sign, q, 0]),Decimal([self.sign, r, ideal_exp])]
|
2665
|
+
end
|
2666
|
+
end
|
2667
|
+
# Here the quotient is too large to be representable
|
2668
|
+
ans = context.exception(DivisionImpossible, 'quotient too large in //, % or divmod')
|
2669
|
+
return [ans, ans]
|
2670
|
+
|
2671
|
+
end
|
2672
|
+
|
2673
|
+
def _divide_floor(other, context)
|
2674
|
+
context = Decimal.define_context(context)
|
2675
|
+
sign = self.sign * other.sign
|
2676
|
+
if other.infinite?
|
2677
|
+
ideal_exp = self.integral_exponent
|
2678
|
+
else
|
2679
|
+
ideal_exp = [self.integral_exponent, other.integral_exponent].min
|
2680
|
+
end
|
2681
|
+
|
2682
|
+
expdiff = self.adjusted_exponent - other.adjusted_exponent
|
2683
|
+
if self.zero? || other.infinite? || (expdiff <= -2)
|
2684
|
+
return [Decimal.new([sign, 0, 0]), _rescale(ideal_exp, context.rounding)]
|
2685
|
+
end
|
2686
|
+
if (expdiff <= context.precision) || context.exact?
|
2687
|
+
self_coeff = self.integral_significand*self.sign
|
2688
|
+
other_coeff = other.integral_significand*other.sign
|
2689
|
+
de = self.integral_exponent - other.integral_exponent
|
2690
|
+
if de >= 0
|
2691
|
+
self_coeff = Decimal.int_mult_radix_power(self_coeff, de)
|
2692
|
+
else
|
2693
|
+
other_coeff = Decimal.int_mult_radix_power(other_coeff, -de)
|
2694
|
+
end
|
2695
|
+
q, r = self_coeff.divmod(other_coeff)
|
2696
|
+
if r<0
|
2697
|
+
r = -r
|
2698
|
+
rs = -1
|
2699
|
+
else
|
2700
|
+
rs = +1
|
2701
|
+
end
|
2702
|
+
if q<0
|
2703
|
+
q = -q
|
2704
|
+
qs = -1
|
2705
|
+
else
|
2706
|
+
qs = +1
|
2707
|
+
end
|
2708
|
+
if (q < Decimal.int_radix_power(context.precision)) || context.exact?
|
2709
|
+
return [Decimal([qs, q, 0]),Decimal([rs, r, ideal_exp])]
|
2710
|
+
end
|
2711
|
+
end
|
2712
|
+
# Here the quotient is too large to be representable
|
2713
|
+
ans = context.exception(DivisionImpossible, 'quotient too large in //, % or divmod')
|
2714
|
+
return [ans, ans]
|
2715
|
+
|
2716
|
+
end
|
2717
|
+
|
2718
|
+
# Convert a numeric value to decimal (internal use)
|
2719
|
+
def Decimal._convert(x, error=true)
|
2720
|
+
case x
|
2721
|
+
when Decimal
|
2722
|
+
x
|
2723
|
+
when *Decimal.context.coercible_types
|
2724
|
+
Decimal.new(x)
|
2725
|
+
else
|
2726
|
+
raise TypeError, "Unable to convert #{x.class} to Decimal" if error
|
2727
|
+
nil
|
2728
|
+
end
|
2729
|
+
end
|
2730
|
+
|
2731
|
+
# Parse numeric text literals (internal use)
|
2732
|
+
def _parser(txt)
|
2733
|
+
md = /^\s*([-+])?(?:(?:(\d+)(?:\.(\d*))?|\.(\d+))(?:[eE]([-+]?\d+))?|Inf(?:inity)?|(s)?NaN(\d*))\s*$/i.match(txt)
|
2734
|
+
if md
|
2735
|
+
OpenStruct.new :sign=>md[1], :int=>md[2], :frac=>md[3], :onlyfrac=>md[4], :exp=>md[5],
|
2736
|
+
:signal=>md[6], :diag=>md[7]
|
2737
|
+
end
|
2738
|
+
end
|
2739
|
+
|
2740
|
+
end
|
2741
|
+
|
2742
|
+
# Decimal constructor. See Decimal#new for the parameters.
|
2743
|
+
# If a Decimal is passed a reference to it is returned (no new object is created).
|
2744
|
+
def Decimal(*args)
|
2745
|
+
if args.size==1 && args.first.instance_of?(Decimal)
|
2746
|
+
args.first
|
2747
|
+
else
|
2748
|
+
Decimal.new(*args)
|
2749
|
+
end
|
2750
|
+
end
|