ruby-decimal 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ == 0.1.0 2009-06-19
2
+
3
+ * Initial release
@@ -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.
@@ -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
+
@@ -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:
@@ -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
@@ -0,0 +1,4 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'decimal/support'
4
+ require 'decimal/decimal'
@@ -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