ruby-decimal 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +18 -3
- data/Manifest.txt +4 -0
- data/README.txt +107 -16
- data/lib/decimal/decimal.rb +294 -104
- data/lib/decimal/shortcut.rb +6 -0
- data/lib/decimal/version.rb +1 -1
- data/test/all_tests.rb +3 -0
- data/test/test_basic.rb +0 -2
- data/test/test_define_conversions.rb +12 -0
- data/test/test_epsilon.rb +34 -0
- data/test/test_ulp.rb +76 -0
- metadata +7 -2
data/History.txt
CHANGED
@@ -1,9 +1,24 @@
|
|
1
1
|
== 0.1.0 2009-06-19
|
2
2
|
|
3
|
-
* Initial release
|
3
|
+
* Initial release
|
4
4
|
|
5
5
|
== 0.2.0 2009-06-21
|
6
6
|
|
7
|
-
*
|
8
|
-
|
7
|
+
* New functions implemented:
|
8
|
+
exp(), ln(), log10(), power().
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
* Bug fixes:
|
13
|
+
- Decimal#inspect was always producing debug information; now it uses $DEBUG
|
14
|
+
- Raising some exceptions caused error, because too many parameters were being
|
15
|
+
passed to the exception's base class initializer.
|
16
|
+
|
17
|
+
* New functionality:
|
18
|
+
- ulp (unit in the last place)
|
19
|
+
- normalize (in the classic sense, not in the original GDA sense)
|
20
|
+
- maximum_finite, minimum_normal, minimum_nonzero value decimal constructors
|
21
|
+
- epsilon, strict_epsilon, half_epsilon
|
22
|
+
- setting context exponent limits with elimit
|
23
|
+
- require decimal/shortcut to use D for Decimal
|
9
24
|
|
data/Manifest.txt
CHANGED
@@ -7,6 +7,7 @@ lib/decimal.rb
|
|
7
7
|
lib/decimal/version.rb
|
8
8
|
lib/decimal/decimal.rb
|
9
9
|
lib/decimal/support.rb
|
10
|
+
lib/decimal/shortcut.rb
|
10
11
|
setup.rb
|
11
12
|
test/all_tests.rb
|
12
13
|
test/helper.rb
|
@@ -21,4 +22,7 @@ test/test_round.rb
|
|
21
22
|
test/test_exact.rb
|
22
23
|
test/test_to_int.rb
|
23
24
|
test/test_to_rf.rb
|
25
|
+
test/test_epsilon.rb
|
26
|
+
test/test_ulp.rb
|
27
|
+
test/test_odd_even.rb
|
24
28
|
|
data/README.txt
CHANGED
@@ -20,10 +20,10 @@ and the revised IEEE 754 standard (IEEE 754-2008).
|
|
20
20
|
|
21
21
|
= Examples of use
|
22
22
|
|
23
|
-
To install the library use gem from the command line: (you may not need sudo)
|
23
|
+
To install the library use gem from the command line: (you may not need +sudo+)
|
24
24
|
sudo gem install ruby-decimal
|
25
25
|
|
26
|
-
Then require the library in your code
|
26
|
+
Then require the library in your code (if it fails you may need to <tt>require 'rubygems'</tt> first)
|
27
27
|
require 'decimal'
|
28
28
|
|
29
29
|
Now we can use the Decimal class simply like this:
|
@@ -79,8 +79,8 @@ And individual parameters can be assigned like this:
|
|
79
79
|
puts Decimal.context.precision -> 9
|
80
80
|
puts Decimal.context.rounding -> half_even
|
81
81
|
Decimal.context(:rounding=>:down) do |context|
|
82
|
-
puts context.precision
|
83
|
-
puts context.rounding
|
82
|
+
puts context.precision -> 9
|
83
|
+
puts context.rounding -> down
|
84
84
|
end
|
85
85
|
|
86
86
|
Contexts created with the Decimal::Context() constructor
|
@@ -173,10 +173,48 @@ an interface compatible with the Ruby interface of Float:
|
|
173
173
|
puts Decimal('101.5').truncate -> 101
|
174
174
|
|
175
175
|
==Special values
|
176
|
-
|
176
|
+
|
177
|
+
In addition to finite numbers, a Decimal object can represent some special values:
|
178
|
+
* Infinity (+Infinity, -Infinity). The method Decimal#infinite? returns true for these to values.
|
179
|
+
Decimal.infinity Decimal.infinity(-1) can be used to get these values.
|
180
|
+
* NaN (not a number) represents indefined results. The method Decimal#nan? returns true for it and
|
181
|
+
Decimal.nan can be used to obtain it. There is a variant, sNaN (signaling NaN) that casues
|
182
|
+
an invalid operation condition if used; it can be detected with Decimal.snan?.
|
183
|
+
A NaN can also include diagnostic information in its sign and coefficient.
|
184
|
+
|
185
|
+
Any of the special values can be detected with Decimal#special?
|
186
|
+
Finite numbers can be clasified with
|
187
|
+
these methods:
|
188
|
+
* Decimal#zero? detects a zero value (note that there are two zero values: +0 and -0)
|
189
|
+
* Decimal#normal? detects normal values: those whose adjusted exponents are not less than the the emin.
|
190
|
+
* Decimal#subnormal? detects subnormal values: those whose adjusted exponents are less than the the emin.
|
177
191
|
|
178
192
|
==Exceptions
|
179
|
-
|
193
|
+
|
194
|
+
Exceptional conditions that may arise during operations have corresponding classes that represent them:
|
195
|
+
* Decimal::InvalidOperation
|
196
|
+
* Decimal::DivisionByZero
|
197
|
+
* Decimal::DivisionImpossible
|
198
|
+
* Decimal::DivisionUndefined
|
199
|
+
* Decimal::Inexact
|
200
|
+
* Decimal::Overflow
|
201
|
+
* Decimal::Underflow
|
202
|
+
* Decimal::Clamped
|
203
|
+
* Decimal::InvalidContext
|
204
|
+
* Decimal::Rounded
|
205
|
+
* Decimal::Subnormal
|
206
|
+
* Decimal::ConversionSyntax
|
207
|
+
|
208
|
+
For each condition, a flag and a trap (boolean values) exist in the context.
|
209
|
+
When a condition occurs, the corresponding flag in the context takes the value true (and remains
|
210
|
+
set until cleared) and a exception is raised if the corresponding trap has the value true.
|
211
|
+
|
212
|
+
Decimal.context.traps[Decimal::DivisionByZero] = false
|
213
|
+
Decimal.context.flags[Decimal::DivisionByZero] = false
|
214
|
+
puts Decimal(1)/Decimal(0) -> Infinity
|
215
|
+
puts Decimal.context.flags[Decimal::DivisionByZero] -> true
|
216
|
+
Decimal.context.traps[Decimal::DivisionByZero] = true
|
217
|
+
puts Decimal(1)/Decimal(0) -> Exception : Decimal::DivisionByZero
|
180
218
|
|
181
219
|
==Numerical conversion
|
182
220
|
|
@@ -206,6 +244,7 @@ Or with a generic method:
|
|
206
244
|
Conversion is also possible to Float:
|
207
245
|
puts Decimal('1.1').to_f -> 1.1
|
208
246
|
puts Decimal('1.1').convert_to(Float) -> 1.1
|
247
|
+
puts Float(Decimal('1.1')) -> 1.1
|
209
248
|
|
210
249
|
And with GDAS style operations:
|
211
250
|
|
@@ -252,6 +291,46 @@ Note that the conversion we've defined depends on the context precision:
|
|
252
291
|
|
253
292
|
Decimal.local_context(:precision=>12) { puts Decimal(0.1) } -> 0.100000000000
|
254
293
|
|
294
|
+
Decimal.local_context(:exact=>true) { puts Decimal(0.1) } -> 0.1000000000000000055511151231257827021181583404541015625
|
295
|
+
|
296
|
+
== Abbreviation
|
297
|
+
|
298
|
+
The use of Decimal can be made less verbose by requiring:
|
299
|
+
|
300
|
+
require 'decimal/shortcut'
|
301
|
+
|
302
|
+
This file defines +D+ as a synonym for +Decimal+:
|
303
|
+
|
304
|
+
D.context.precision = 3
|
305
|
+
puts +D('1.234') -> 1.23
|
306
|
+
|
307
|
+
== Error analysis
|
308
|
+
|
309
|
+
The Decimal#ulp() method returns the value of a "unit in the last place" for a given number
|
310
|
+
|
311
|
+
D.context.precision = 4
|
312
|
+
puts D('1.5').ulp -> 0.001
|
313
|
+
puts D('1.5E10').ulp -> 1E+7
|
314
|
+
|
315
|
+
Whe can compute the error in ulps of an approximation +aprx+ to correclty rounded value +exct+ with:
|
316
|
+
|
317
|
+
def ulps(exct, aprx)
|
318
|
+
(aprx-exct).abs/exct.ulp
|
319
|
+
end
|
320
|
+
|
321
|
+
puts ulps(Decimal('0.5000'), Decimal('0.5003')) -> 3
|
322
|
+
puts ulps(Decimal('0.5000'), Decimal('0.4997')) -> 3
|
323
|
+
|
324
|
+
puts ulps(Decimal('0.1000'), Decimal('0.1003')) -> 3E+1
|
325
|
+
puts ulps(Decimal('0.1000'), Decimal('0.0997')) -> 3E+1
|
326
|
+
|
327
|
+
puts ulps(Decimal(1), Decimal(10).next_minus) -> 8.999E+4
|
328
|
+
puts ulps(Decimal(1), Decimal(10).next_plus) -> 9.01E+4
|
329
|
+
|
330
|
+
Note that in the definition of ulps we use exct.ulp. If we had use aprx.ulp Decimal(10).next_plus
|
331
|
+
would seem to be a better approximation to Decimal(1) than Decimal(10).next_minus. (Admittedly,
|
332
|
+
such bad approximations should not be common.)
|
333
|
+
|
255
334
|
== More Information
|
256
335
|
|
257
336
|
Consult the documentation for the classes Decimal and Decimal::Context.
|
@@ -265,25 +344,33 @@ EXPAND-
|
|
265
344
|
Decimal solves some of the difficulties of using BigDecimal.
|
266
345
|
|
267
346
|
One of the major problems with BigDecimal is that it's not easy to control the number of
|
268
|
-
significant digits
|
269
|
-
divisions will need to be passed precision explicitly or
|
270
|
-
|
347
|
+
significant digits of the results. While addition, subtraction and multiplication are exact (unless a limit is used),
|
348
|
+
divisions will need to be passed precision explicitly or else an indeterminate number of significant digits will be lost.
|
349
|
+
Part of the problem is that numbers don't keep track of its precision (0.1000 is not distinguishable from 0.1.)
|
350
|
+
|
351
|
+
With Decimal, Context objects are used to specify the exact number of digits to be used for all operations making
|
352
|
+
the code cleaner and the results more easily predictable.
|
353
|
+
|
271
354
|
Decimal.context.precision = 10
|
272
355
|
puts Decimal(1)/Decimal(3)
|
273
356
|
Contexts are thread-safe and can be used for individual operations:
|
274
|
-
puts Decimal(1).divide(Decimal(e), Decimal::Context
|
357
|
+
puts Decimal(1).divide(Decimal(e), Decimal::Context(:precision=>4))
|
358
|
+
Which can be abbreviated:
|
359
|
+
puts Decimal(1).divide(Decimal(e), :precision=>4)
|
275
360
|
Or use locally in a block without affecting other code:
|
276
|
-
Decimal.
|
361
|
+
Decimal.context {
|
277
362
|
Decimal.context.precision = 3
|
278
363
|
puts Decimal(1)/Decimal(3)
|
279
364
|
}
|
280
365
|
puts Decimal.context.precision
|
366
|
+
Which can also be abbreviated:
|
367
|
+
Decimal.context(:precision=>3) { puts Decimal(1)/Decimal(3) }
|
281
368
|
|
282
369
|
This allows in general to write simpler code; e.g. this is an exponential function, adapted from the
|
283
370
|
'recipes' in Python's Decimal:
|
284
|
-
def exp(x,c=nil)
|
371
|
+
def exp(x, c=nil)
|
285
372
|
i, lasts, s, fact, num = 0, 0, 1, 1, 1
|
286
|
-
Decimal.
|
373
|
+
Decimal.context(c) do |context|
|
287
374
|
context.precision += 2
|
288
375
|
while s != lasts
|
289
376
|
lasts = s
|
@@ -295,9 +382,11 @@ This allows in general to write simpler code; e.g. this is an exponential functi
|
|
295
382
|
end
|
296
383
|
return +s
|
297
384
|
end
|
385
|
+
|
298
386
|
The final unary + applied to the result forces it to be rounded to the current precision
|
299
387
|
(because we have computed it with two extra digits)
|
300
|
-
The result of this method does not have trailing
|
388
|
+
The result of this method does not have trailing non-significant digits, as is common with BigDecimal
|
389
|
+
(e.g. in the exp implementation available in the standard Ruby library, in bigdecimal/math)
|
301
390
|
|
302
391
|
--
|
303
392
|
EXPAND+
|
@@ -305,5 +394,7 @@ EXPAND+
|
|
305
394
|
|
306
395
|
= Roadmap
|
307
396
|
|
308
|
-
* Version 0.3.0: Implement the
|
309
|
-
|
397
|
+
* Version 0.3.0: Implement the missing GDA functions:
|
398
|
+
rotate, shift, trim, and, or, xor, invert,
|
399
|
+
max, min, maxmag, minmag, comparetotal, comparetotmag
|
400
|
+
|
data/lib/decimal/decimal.rb
CHANGED
@@ -109,7 +109,7 @@ class Decimal
|
|
109
109
|
end
|
110
110
|
def initialize(context=nil, *args)
|
111
111
|
@value = args.first if args.size>0
|
112
|
-
super
|
112
|
+
super context
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
@@ -123,7 +123,7 @@ class Decimal
|
|
123
123
|
end
|
124
124
|
def initialize(context=nil, sign=nil, *args)
|
125
125
|
@sign = sign
|
126
|
-
super
|
126
|
+
super context
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
@@ -203,7 +203,7 @@ class Decimal
|
|
203
203
|
end
|
204
204
|
def initialize(context=nil, sign=nil, *args)
|
205
205
|
@sign = sign
|
206
|
-
super
|
206
|
+
super context
|
207
207
|
end
|
208
208
|
end
|
209
209
|
|
@@ -295,11 +295,15 @@ class Decimal
|
|
295
295
|
# * :rounding : one of :half_even, :half_down, :half_up, :floor,
|
296
296
|
# :ceiling, :down, :up, :up05
|
297
297
|
# * :precision : number of digits (or 0 for exact precision)
|
298
|
-
# * :exact : true
|
298
|
+
# * :exact : if true precision is ignored and Inexact conditions are trapped,
|
299
|
+
# if :quiet it set exact precision but no trapping;
|
299
300
|
# * :traps : a Flags object with the exceptions to be trapped
|
300
301
|
# * :flags : a Flags object with the raised flags
|
301
302
|
# * :ignored_flags : a Flags object with the exceptions to be ignored
|
302
|
-
# * :emin, :emax : minimum and maximum exponents
|
303
|
+
# * :emin, :emax : minimum and maximum adjusted exponents
|
304
|
+
# * :elimit : the exponent limits can also be defined by a single value;
|
305
|
+
# if positive it is taken as emax and emin=1-emax; otherwiae it is
|
306
|
+
# taken as emin and emax=1-emin. Such limits comply with IEEE 754-2008
|
303
307
|
# * :capitals : (true or false) to use capitals in text representations
|
304
308
|
# * :clamp : (true or false) enables clamping
|
305
309
|
#
|
@@ -310,6 +314,9 @@ class Decimal
|
|
310
314
|
base = options.shift
|
311
315
|
copy_from base
|
312
316
|
else
|
317
|
+
@rounding = @emin = @emax = nil
|
318
|
+
@capitals = false
|
319
|
+
@clamp = false
|
313
320
|
@ignored_flags = Decimal::Flags()
|
314
321
|
@traps = Decimal::Flags()
|
315
322
|
@flags = Decimal::Flags()
|
@@ -346,16 +353,25 @@ class Decimal
|
|
346
353
|
@ignored_flags.clear(*flags)
|
347
354
|
end
|
348
355
|
|
349
|
-
# 'tiny'
|
356
|
+
# 'tiny' exponent (emin - precision + 1)
|
357
|
+
# is the minimum valid value for the (integral) exponent
|
350
358
|
def etiny
|
351
359
|
emin - precision + 1
|
352
360
|
end
|
353
361
|
|
354
|
-
#
|
362
|
+
# top exponent (emax - precision + 1)
|
363
|
+
# is the maximum valid value for the (integral) exponent
|
355
364
|
def etop
|
356
365
|
emax - precision + 1
|
357
366
|
end
|
358
367
|
|
368
|
+
# Set the exponent limits, according to IEEE 754-2008
|
369
|
+
# if e > 0 it is taken as emax and emin=1-emax
|
370
|
+
# if e < 0 it is taken as emin and emax=1-emin
|
371
|
+
def elimit=(e)
|
372
|
+
@emin, @emax = [elimit, 1-elimit].sort
|
373
|
+
end
|
374
|
+
|
359
375
|
# synonym for precision()
|
360
376
|
def digits
|
361
377
|
self.precision
|
@@ -420,6 +436,9 @@ class Decimal
|
|
420
436
|
@traps = Decimal::Flags(options[:traps]) unless options[:traps].nil?
|
421
437
|
@flags = Decimal::Flags(options[:flags]) unless options[:flags].nil?
|
422
438
|
@ignored_flags = Decimal::Flags(options[:ignored_flags]) unless options[:ignored_flags].nil?
|
439
|
+
if elimit=options[:elimit]
|
440
|
+
@emin, @emax = [elimit, 1-elimit].sort
|
441
|
+
end
|
423
442
|
@emin = options[:emin] unless options[:emin].nil?
|
424
443
|
@emax = options[:emax] unless options[:emax].nil?
|
425
444
|
@capitals = options[:capitals ] unless options[:capitals ].nil?
|
@@ -526,6 +545,12 @@ class Decimal
|
|
526
545
|
_convert(x).reduce(self)
|
527
546
|
end
|
528
547
|
|
548
|
+
# normalizes so that the coefficient has precision digits
|
549
|
+
# (this is not the old GDA normalize function)
|
550
|
+
def normalize(x)
|
551
|
+
_convert(x).reduce(self)
|
552
|
+
end
|
553
|
+
|
529
554
|
# Adjusted exponent of x returned as a Decimal value.
|
530
555
|
def logb(x)
|
531
556
|
_convert(x).logb(self)
|
@@ -562,14 +587,14 @@ class Decimal
|
|
562
587
|
# normalized to precision digits. (minimum exponent)
|
563
588
|
def normalized_integral_exponent(x)
|
564
589
|
x = _convert(x)
|
565
|
-
x.
|
590
|
+
x.exponent - (precision - x.number_of_digits)
|
566
591
|
end
|
567
592
|
|
568
593
|
# Significand normalized to precision digits
|
569
594
|
# x == normalized_integral_significand(x) * radix**(normalized_integral_exponent)
|
570
595
|
def normalized_integral_significand(x)
|
571
596
|
x = _convert(x)
|
572
|
-
x.
|
597
|
+
x.coefficient*(Decimal.int_radix_power(precision - x.number_of_digits))
|
573
598
|
end
|
574
599
|
|
575
600
|
# Returns both the (signed) normalized integral significand and the corresponding exponent
|
@@ -726,13 +751,87 @@ class Decimal
|
|
726
751
|
_convert(x).next_toward(y, self)
|
727
752
|
end
|
728
753
|
|
754
|
+
# ulp (unit in the last place) according to the definition proposed by J.M. Muller in
|
755
|
+
# "On the definition of ulp(x)" INRIA No. 5504
|
756
|
+
def ulp(x=nil)
|
757
|
+
x ||= 1
|
758
|
+
_convert(x).ulp(self)
|
759
|
+
end
|
760
|
+
|
761
|
+
# Some singular Decimal values that depend on the context
|
762
|
+
|
763
|
+
# Maximum finite number
|
764
|
+
def maximum_finite(sign=+1)
|
765
|
+
return exception(InvalidOperation, "Exact context maximum finite value") if exact?
|
766
|
+
# equals +Decimal(+1, 1, emax)
|
767
|
+
# equals Decimal.infinity.next_minus(self)
|
768
|
+
Decimal(sign, Decimal.int_radix_power(precision)-1, etop)
|
769
|
+
end
|
770
|
+
|
771
|
+
# Minimum positive normal number
|
772
|
+
def minimum_normal(sign=+1)
|
773
|
+
return exception(InvalidOperation, "Exact context maximum normal value") if exact?
|
774
|
+
Decimal(sign, 1, emin)
|
775
|
+
end
|
776
|
+
|
777
|
+
# Maximum subnormal number
|
778
|
+
def maximum_subnormal(sign=+1)
|
779
|
+
return exception(InvalidOperation, "Exact context maximum subnormal value") if exact?
|
780
|
+
# equals mininum_normal.next_minus(self)
|
781
|
+
Decimal(sign, Decimal.int_radix_power(precision-1)-1, etiny)
|
782
|
+
end
|
783
|
+
|
784
|
+
# Minimum nonzero positive number (minimum positive subnormal)
|
785
|
+
def minimum_nonzero(sign=+1)
|
786
|
+
return exception(InvalidOperation, "Exact context minimum nonzero value") if exact?
|
787
|
+
Decimal(sign, 1, etiny)
|
788
|
+
end
|
789
|
+
|
790
|
+
# This is the difference between 1 and the smallest Decimal
|
791
|
+
# value greater than 1: (Decimal(1).next_plus - Decimal(1))
|
792
|
+
def epsilon(sign=+1)
|
793
|
+
return exception(InvalidOperation, "Exact context epsilon") if exact?
|
794
|
+
Decimal(sign, 1, 1-precision)
|
795
|
+
end
|
796
|
+
|
797
|
+
# The strict epsilon is the smallest value that produces something different from 1
|
798
|
+
# wehen added to 1. It may be smaller than the general epsilon, because
|
799
|
+
# of the particular rounding rules used.
|
800
|
+
def strict_epsilon(sign=+1)
|
801
|
+
return exception(InvalidOperation, "Exact context strict epsilon") if exact?
|
802
|
+
coeff = 1
|
803
|
+
exp = 1-precision
|
804
|
+
# assume radix is even (Decimal.radix%2 == 0)
|
805
|
+
case rounding
|
806
|
+
when :down, :floor
|
807
|
+
exp = 1-precision
|
808
|
+
coeff = 1
|
809
|
+
when :half_even, :half_down #, :up # :up # :down, :half_down, :up05, :floor
|
810
|
+
exp = 1-2*precision
|
811
|
+
coeff = 1 + Decimal.int_radix_power(precision)/2
|
812
|
+
when :half_up
|
813
|
+
exp = 1-2*precision
|
814
|
+
coeff = Decimal.int_radix_power(precision)/2
|
815
|
+
when :up, :ceiling, :up05
|
816
|
+
return minimum_nonzero(sign)
|
817
|
+
end
|
818
|
+
return Decimal(sign, coeff, exp)
|
819
|
+
end
|
820
|
+
|
821
|
+
# This is the maximum relative error corresponding to 1/2 ulp:
|
822
|
+
# (radix/2)*radix**(-precision) == epsilon/2
|
823
|
+
# This is called "machine epsilon" in Goldberg's "What Every Computer Scientist..."
|
824
|
+
def half_epsilon(sign=+1)
|
825
|
+
Decimal(sign, Decimal.radix/2, -precision)
|
826
|
+
end
|
827
|
+
|
729
828
|
def to_s
|
730
829
|
inspect
|
731
830
|
end
|
732
831
|
|
733
832
|
def inspect
|
734
833
|
"<#{self.class}:\n" +
|
735
|
-
instance_variables.map { |v| " #{v}: #{eval(v)}"}.join("\n") +
|
834
|
+
instance_variables.map { |v| " #{v}: #{eval(v).inspect}"}.join("\n") +
|
736
835
|
">\n"
|
737
836
|
end
|
738
837
|
|
@@ -809,10 +908,16 @@ class Decimal
|
|
809
908
|
end
|
810
909
|
|
811
910
|
def update_precision
|
911
|
+
if @emax && !@emin
|
912
|
+
@emin = 1 - @emax
|
913
|
+
elsif @emin && !@emax
|
914
|
+
@emax = 1 - @emin
|
915
|
+
end
|
812
916
|
if @exact || @precision==0
|
917
|
+
quiet = (@exact == :quiet)
|
813
918
|
@exact = true
|
814
919
|
@precision = 0
|
815
|
-
@traps << Inexact
|
920
|
+
@traps << Inexact unless quiet
|
816
921
|
@ignored_flags[Inexact] = false
|
817
922
|
else
|
818
923
|
@traps[Inexact] = false
|
@@ -1003,6 +1108,16 @@ class Decimal
|
|
1003
1108
|
# * Given a precision p = 8, the normalized integral representation is 12040000 with exponent -9
|
1004
1109
|
# * The normalized fractional representation is 0.1204 with exponent -1
|
1005
1110
|
#
|
1111
|
+
# ==Exponent limits
|
1112
|
+
#
|
1113
|
+
# The (integral) exponent e must be within this limits: etiny <= e <= etop
|
1114
|
+
# The adjusted exponent a must: emin <= a <= emax
|
1115
|
+
# emin, emax are the limite of the exponent shown in scientific notation and are use to defined
|
1116
|
+
# the exponent limits in the contexts.
|
1117
|
+
# etiny = emin-precision+1 and etop=emax-precision+1 are the limits of the internal exponent.
|
1118
|
+
# Note that for significands with less than precision digits we can use exponents greater than etop
|
1119
|
+
# without causing overflow: +Decimal(+1,1,emax) == Decimal(+1,K,etop) where K=10**(precision-1)
|
1120
|
+
#
|
1006
1121
|
# =Interoperatibility with other numeric types
|
1007
1122
|
#
|
1008
1123
|
# For some numeric types implicit conversion to Decimal is defined through these methods:
|
@@ -1044,14 +1159,26 @@ class Decimal
|
|
1044
1159
|
arg.delete :exponent
|
1045
1160
|
context ||= arg
|
1046
1161
|
end
|
1162
|
+
args = args.first if args.size==1 && args.first.is_a?(Array)
|
1047
1163
|
|
1048
1164
|
context = Decimal.define_context(context)
|
1049
1165
|
|
1050
1166
|
case args.size
|
1051
1167
|
when 3
|
1168
|
+
# internal representation
|
1052
1169
|
@sign, @coeff, @exp = args
|
1053
1170
|
# TO DO: validate
|
1054
1171
|
|
1172
|
+
when 2
|
1173
|
+
# signed integer and scale
|
1174
|
+
@coeff, @exp = args
|
1175
|
+
if @coeff < 0
|
1176
|
+
@sign = -1
|
1177
|
+
@coeff = -@coeff
|
1178
|
+
else
|
1179
|
+
@sign = +1
|
1180
|
+
end
|
1181
|
+
|
1055
1182
|
when 1
|
1056
1183
|
arg = args.first
|
1057
1184
|
case arg
|
@@ -1108,19 +1235,17 @@ class Decimal
|
|
1108
1235
|
@exp = :inf
|
1109
1236
|
end
|
1110
1237
|
end
|
1111
|
-
when Array
|
1112
|
-
@sign, @coeff, @exp = arg
|
1113
1238
|
else
|
1114
1239
|
raise TypeError, "invalid argument #{arg.inspect}"
|
1115
1240
|
end
|
1116
1241
|
else
|
1117
|
-
raise ArgumentError, "wrong number of arguments (#{args.size} for 1 or 3)"
|
1242
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 1, 2 or 3)"
|
1118
1243
|
end
|
1119
1244
|
end
|
1120
1245
|
|
1121
1246
|
# Returns the internal representation of the number, composed of:
|
1122
1247
|
# * a sign which is +1 for plus and -1 for minus
|
1123
|
-
# * a coefficient (significand) which is
|
1248
|
+
# * a coefficient (significand) which is a nonnegative integer
|
1124
1249
|
# * an exponent (an integer) or :inf, :nan or :snan for special values
|
1125
1250
|
# The value of non-special numbers is sign*coefficient*10^exponent
|
1126
1251
|
def split
|
@@ -1289,7 +1414,7 @@ class Decimal
|
|
1289
1414
|
return Decimal(other) if other.infinite?
|
1290
1415
|
end
|
1291
1416
|
|
1292
|
-
exp = [self.
|
1417
|
+
exp = [self.exponent, other.exponent].min
|
1293
1418
|
negativezero = (context.rounding == ROUND_FLOOR && self.sign != other.sign)
|
1294
1419
|
|
1295
1420
|
if self.zero? && other.zero?
|
@@ -1300,12 +1425,12 @@ class Decimal
|
|
1300
1425
|
end
|
1301
1426
|
|
1302
1427
|
if self.zero?
|
1303
|
-
exp = [exp, other.
|
1428
|
+
exp = [exp, other.exponent - context.precision - 1].max unless context.exact?
|
1304
1429
|
return other._rescale(exp, context.rounding)._fix(context)
|
1305
1430
|
end
|
1306
1431
|
|
1307
1432
|
if other.zero?
|
1308
|
-
exp = [exp, self.
|
1433
|
+
exp = [exp, self.exponent - context.precision - 1].max unless context.exact?
|
1309
1434
|
return self._rescale(exp, context.rounding)._fix(context)
|
1310
1435
|
end
|
1311
1436
|
|
@@ -1313,8 +1438,8 @@ class Decimal
|
|
1313
1438
|
|
1314
1439
|
result_sign = result_coeff = result_exp = nil
|
1315
1440
|
if op1.sign != op2.sign
|
1316
|
-
return ans = Decimal.new([negativezero ? -1 : +1, 0, exp])._fix(context) if op1.
|
1317
|
-
op1,op2 = op2,op1 if op1.
|
1441
|
+
return ans = Decimal.new([negativezero ? -1 : +1, 0, exp])._fix(context) if op1.coefficient == op2.coefficient
|
1442
|
+
op1,op2 = op2,op1 if op1.coefficient < op2.coefficient
|
1318
1443
|
result_sign = op1.sign
|
1319
1444
|
op1,op2 = op1.copy_negate, op2.copy_negate if result_sign < 0
|
1320
1445
|
elsif op1.sign < 0
|
@@ -1325,12 +1450,12 @@ class Decimal
|
|
1325
1450
|
end
|
1326
1451
|
|
1327
1452
|
if op2.sign == +1
|
1328
|
-
result_coeff = op1.
|
1453
|
+
result_coeff = op1.coefficient + op2.coefficient
|
1329
1454
|
else
|
1330
|
-
result_coeff = op1.
|
1455
|
+
result_coeff = op1.coefficient - op2.coefficient
|
1331
1456
|
end
|
1332
1457
|
|
1333
|
-
result_exp = op1.
|
1458
|
+
result_exp = op1.exponent
|
1334
1459
|
|
1335
1460
|
return Decimal([result_sign, result_coeff, result_exp])._fix(context)
|
1336
1461
|
|
@@ -1369,13 +1494,13 @@ class Decimal
|
|
1369
1494
|
end
|
1370
1495
|
end
|
1371
1496
|
|
1372
|
-
resultexp = self.
|
1497
|
+
resultexp = self.exponent + other.exponent
|
1373
1498
|
|
1374
1499
|
return Decimal([resultsign, 0, resultexp])._fix(context) if self.zero? || other.zero?
|
1375
|
-
#return Decimal([resultsign, other.
|
1376
|
-
#return Decimal([resultsign, self.
|
1500
|
+
#return Decimal([resultsign, other.coefficient, resultexp])._fix(context) if self.coefficient==1
|
1501
|
+
#return Decimal([resultsign, self.coefficient, resultexp])._fix(context) if other.coefficient==1
|
1377
1502
|
|
1378
|
-
return Decimal([resultsign, other.
|
1503
|
+
return Decimal([resultsign, other.coefficient*self.coefficient, resultexp])._fix(context)
|
1379
1504
|
|
1380
1505
|
end
|
1381
1506
|
|
@@ -1403,22 +1528,22 @@ class Decimal
|
|
1403
1528
|
end
|
1404
1529
|
|
1405
1530
|
if self.zero?
|
1406
|
-
exp = self.
|
1531
|
+
exp = self.exponent - other.exponent
|
1407
1532
|
coeff = 0
|
1408
1533
|
else
|
1409
1534
|
prec = context.exact? ? self.number_of_digits + 4*other.number_of_digits : context.precision # this assumes radix==10
|
1410
1535
|
shift = other.number_of_digits - self.number_of_digits + prec + 1
|
1411
|
-
exp = self.
|
1536
|
+
exp = self.exponent - other.exponent - shift
|
1412
1537
|
if shift >= 0
|
1413
|
-
coeff, remainder = (self.
|
1538
|
+
coeff, remainder = (self.coefficient*Decimal.int_radix_power(shift)).divmod(other.coefficient)
|
1414
1539
|
else
|
1415
|
-
coeff, remainder = self.
|
1540
|
+
coeff, remainder = self.coefficient.divmod(other.coefficient*Decimal.int_radix_power(-shift))
|
1416
1541
|
end
|
1417
1542
|
if remainder != 0
|
1418
1543
|
return context.exception(Inexact) if context.exact?
|
1419
1544
|
coeff += 1 if (coeff%(Decimal.radix/2)) == 0
|
1420
1545
|
else
|
1421
|
-
ideal_exp = self.
|
1546
|
+
ideal_exp = self.exponent - other.exponent
|
1422
1547
|
while (exp < ideal_exp) && ((coeff % Decimal.radix)==0)
|
1423
1548
|
coeff /= Decimal.radix
|
1424
1549
|
exp += 1
|
@@ -1794,7 +1919,7 @@ class Decimal
|
|
1794
1919
|
return Decimal.new(self)._fix(context)
|
1795
1920
|
end
|
1796
1921
|
|
1797
|
-
ideal_exp = [self.
|
1922
|
+
ideal_exp = [self.exponent, other.exponent].min
|
1798
1923
|
if self.zero?
|
1799
1924
|
return Decimal([self.sign, 0, ideal_exp])._fix(context)
|
1800
1925
|
end
|
@@ -1806,9 +1931,9 @@ class Decimal
|
|
1806
1931
|
return self._rescale(ideal_exp, context.rounding)._fix(context)
|
1807
1932
|
end
|
1808
1933
|
|
1809
|
-
self_coeff = self.
|
1810
|
-
other_coeff = other.
|
1811
|
-
de = self.
|
1934
|
+
self_coeff = self.coefficient
|
1935
|
+
other_coeff = other.coefficient
|
1936
|
+
de = self.exponent - other.exponent
|
1812
1937
|
if de >= 0
|
1813
1938
|
self_coeff = Decimal.int_mult_radix_power(self_coeff, de)
|
1814
1939
|
else
|
@@ -1848,15 +1973,30 @@ class Decimal
|
|
1848
1973
|
|
1849
1974
|
exp_max = context.clamp? ? context.etop : context.emax
|
1850
1975
|
end_d = nd = dup.number_of_digits
|
1851
|
-
exp = dup.
|
1852
|
-
coeff = dup.
|
1976
|
+
exp = dup.exponent
|
1977
|
+
coeff = dup.coefficient
|
1853
1978
|
dgs = dup.digits
|
1854
1979
|
while (dgs[end_d-1]==0) && (exp < exp_max)
|
1855
1980
|
exp += 1
|
1856
1981
|
end_d -= 1
|
1857
1982
|
end
|
1858
1983
|
return Decimal.new([dup.sign, coeff/Decimal.int_radix_power(nd-end_d), exp])
|
1984
|
+
end
|
1859
1985
|
|
1986
|
+
# normalizes so that the coefficient has precision digits
|
1987
|
+
# (this is not the old GDA normalize function)
|
1988
|
+
def normalize(context=nil)
|
1989
|
+
context = Decimal.define_context(context)
|
1990
|
+
return Decimal(self) if self.special? || self.zero?
|
1991
|
+
return context.exception(InvalidOperation, "Normalize in exact context") if context.exact?
|
1992
|
+
return context.exception(Subnormal, "Cannot normalize subnormal") if self.subnormal?
|
1993
|
+
min_normal_coeff = Decimal.int_radix_power(context.precision-1)
|
1994
|
+
sign, coeff, exp = self._fix(context).split
|
1995
|
+
while coeff < min_normal_coeff
|
1996
|
+
coeff *= Decimal.radix
|
1997
|
+
exp -= 1
|
1998
|
+
end
|
1999
|
+
Decimal(sign, coeff, exp)
|
1860
2000
|
end
|
1861
2001
|
|
1862
2002
|
# Returns the exponent of the magnitude of the most significant digit.
|
@@ -1881,7 +2021,7 @@ class Decimal
|
|
1881
2021
|
other = _convert(other)
|
1882
2022
|
ans = _check_nans(context, other)
|
1883
2023
|
return ans if ans
|
1884
|
-
return context.exception(InvalidOperation) if other.infinite? || other.
|
2024
|
+
return context.exception(InvalidOperation) if other.infinite? || other.exponent != 0
|
1885
2025
|
unless context.exact?
|
1886
2026
|
liminf = -2 * (context.emax + context.precision)
|
1887
2027
|
limsup = 2 * (context.emax + context.precision)
|
@@ -1996,10 +2136,50 @@ class Decimal
|
|
1996
2136
|
end
|
1997
2137
|
end
|
1998
2138
|
|
2139
|
+
# ulp (unit in the last place) according to the definition proposed by J.M. Muller in
|
2140
|
+
# "On the definition of ulp(x)" INRIA No. 5504
|
2141
|
+
def ulp(context = nil)
|
2142
|
+
context = Decimal.define_context(context)
|
2143
|
+
|
2144
|
+
return context.exception(InvalidOperation, "ulp in exact context") if context.exact?
|
2145
|
+
|
2146
|
+
if self.nan?
|
2147
|
+
return Decimal(self)
|
2148
|
+
elsif self.infinite?
|
2149
|
+
# The ulp here is context.maximum_finite - context.maximum_finite.next_minus
|
2150
|
+
return Decimal(+1, 1, context.etop)
|
2151
|
+
elsif self.zero? || self.adjusted_exponent <= context.emin
|
2152
|
+
# This is the ulp value for self.abs <= context.minimum_normal*Decimal.context
|
2153
|
+
# Here we use it for self.abs < context.minimum_normal*Decimal.context;
|
2154
|
+
# because of the simple exponent check; the remaining cases are handled below.
|
2155
|
+
return context.minimum_nonzero
|
2156
|
+
else
|
2157
|
+
# The next can compute the ulp value for the values that
|
2158
|
+
# self.abs > context.minimum_normal && self.abs <= context.maximum_finite
|
2159
|
+
# The cases self.abs < context.minimum_normal*Decimal.context have been handled above.
|
2160
|
+
|
2161
|
+
# assert self.normal? && self.abs>context.minimum_nonzero
|
2162
|
+
norm = self.normalize
|
2163
|
+
exp = norm.integral_exponent
|
2164
|
+
sig = norm.integral_significand
|
2165
|
+
|
2166
|
+
# Powers of the radix, r**n, are between areas with different ulp values: r**(n-p-1) and r**(n-p)
|
2167
|
+
# (p is context.precision).
|
2168
|
+
# This method and the ulp definitions by Muller, Kahan and Harrison assign the smaller ulp value
|
2169
|
+
# to r**n; the definition by Goldberg assigns it to the larger ulp.
|
2170
|
+
# The next line selects the smaller ulp for powers of the radix:
|
2171
|
+
exp -= 1 if sig == Decimal.int_radix_power(context.precision-1)
|
2172
|
+
|
2173
|
+
return Decimal(+1, 1, exp)
|
2174
|
+
end
|
2175
|
+
end
|
2176
|
+
|
1999
2177
|
def inspect
|
2000
|
-
|
2001
|
-
|
2002
|
-
|
2178
|
+
if $DEBUG
|
2179
|
+
"Decimal('#{self}') [coeff:#{@coeff.inspect} exp:#{@exp.inspect} s:#{@sign.inspect}]"
|
2180
|
+
else
|
2181
|
+
"Decimal('#{self}')"
|
2182
|
+
end
|
2003
2183
|
end
|
2004
2184
|
|
2005
2185
|
# Internal comparison operator: returns -1 if the first number is less than the second,
|
@@ -2033,8 +2213,8 @@ class Decimal
|
|
2033
2213
|
self_adjusted = self.adjusted_exponent
|
2034
2214
|
other_adjusted = other.adjusted_exponent
|
2035
2215
|
if self_adjusted == other_adjusted
|
2036
|
-
self_padded,other_padded = self.
|
2037
|
-
d = self.
|
2216
|
+
self_padded,other_padded = self.coefficient,other.coefficient
|
2217
|
+
d = self.exponent - other.exponent
|
2038
2218
|
if d>0
|
2039
2219
|
self_padded *= Decimal.int_radix_power(d)
|
2040
2220
|
else
|
@@ -2115,12 +2295,12 @@ class Decimal
|
|
2115
2295
|
@coeff.to_s.size
|
2116
2296
|
end
|
2117
2297
|
|
2118
|
-
# Significand as an integer, unsigned
|
2298
|
+
# Significand as an integer, unsigned. Synonym of coefficient
|
2119
2299
|
def integral_significand
|
2120
2300
|
@coeff
|
2121
2301
|
end
|
2122
2302
|
|
2123
|
-
# Exponent of the significand as an integer
|
2303
|
+
# Exponent of the significand as an integer. Synonym of exponent
|
2124
2304
|
def integral_exponent
|
2125
2305
|
# fractional_exponent - number_of_digits
|
2126
2306
|
@exp
|
@@ -2131,7 +2311,17 @@ class Decimal
|
|
2131
2311
|
@sign
|
2132
2312
|
end
|
2133
2313
|
|
2134
|
-
#
|
2314
|
+
# Significand as an integer, unsigned
|
2315
|
+
def coefficient
|
2316
|
+
@coeff
|
2317
|
+
end
|
2318
|
+
|
2319
|
+
# Exponent of the significand as an integer.
|
2320
|
+
def exponent
|
2321
|
+
@exp
|
2322
|
+
end
|
2323
|
+
|
2324
|
+
# Return the value of the number as an signed integer and a scale.
|
2135
2325
|
def to_int_scale
|
2136
2326
|
if special?
|
2137
2327
|
nil
|
@@ -2252,7 +2442,7 @@ class Decimal
|
|
2252
2442
|
return context.exception(InvalidOperation, 'quantize with one INF')
|
2253
2443
|
end
|
2254
2444
|
end
|
2255
|
-
exp = exp.
|
2445
|
+
exp = exp.exponent
|
2256
2446
|
_watched_rescale(exp, context, watch_exp)
|
2257
2447
|
end
|
2258
2448
|
|
@@ -2267,7 +2457,7 @@ class Decimal
|
|
2267
2457
|
if self.special? || other.special?
|
2268
2458
|
return (self.nan? && other.nan?) || (self.infinite? && other.infinite?)
|
2269
2459
|
end
|
2270
|
-
return self.
|
2460
|
+
return self.exponent == other.exponent
|
2271
2461
|
end
|
2272
2462
|
|
2273
2463
|
# Rounds to a nearby integer. May raise Inexact or Rounded.
|
@@ -2392,7 +2582,7 @@ class Decimal
|
|
2392
2582
|
product = Decimal.infinity(self.sign*other.sign)
|
2393
2583
|
end
|
2394
2584
|
else
|
2395
|
-
product = Decimal.new([self.sign*other.sign,self.
|
2585
|
+
product = Decimal.new([self.sign*other.sign,self.coefficient*other.coefficient, self.exponent+other.exponent])
|
2396
2586
|
end
|
2397
2587
|
return product.add(third, context)
|
2398
2588
|
end
|
@@ -2486,7 +2676,7 @@ class Decimal
|
|
2486
2676
|
multiplier = other.to_i
|
2487
2677
|
end
|
2488
2678
|
|
2489
|
-
exp = _self.
|
2679
|
+
exp = _self.exponent * multiplier
|
2490
2680
|
if exp < 1-context.precision
|
2491
2681
|
exp = 1-context.precision
|
2492
2682
|
context.exception Rounded
|
@@ -2551,7 +2741,7 @@ class Decimal
|
|
2551
2741
|
end
|
2552
2742
|
ans = _self._power_exact(other, test_precision)
|
2553
2743
|
if !ans.nil? && (result_sign == -1)
|
2554
|
-
ans = Decimal(-1, ans.
|
2744
|
+
ans = Decimal(-1, ans.coefficient, ans.exponent)
|
2555
2745
|
end
|
2556
2746
|
end
|
2557
2747
|
|
@@ -2562,10 +2752,10 @@ class Decimal
|
|
2562
2752
|
return context.exception(Inexact, "Inexact power") if context.exact?
|
2563
2753
|
|
2564
2754
|
p = context.precision
|
2565
|
-
xc = _self.
|
2566
|
-
xe = _self.
|
2567
|
-
yc = other.
|
2568
|
-
ye = other.
|
2755
|
+
xc = _self.coefficient
|
2756
|
+
xe = _self.exponent
|
2757
|
+
yc = other.coefficient
|
2758
|
+
ye = other.exponent
|
2569
2759
|
yc = -yc if other.sign == -1
|
2570
2760
|
|
2571
2761
|
# compute correctly rounded result: start with precision +3,
|
@@ -2591,7 +2781,7 @@ class Decimal
|
|
2591
2781
|
# pad with zeros up to length context.precision+1 if necessary
|
2592
2782
|
if ans.number_of_digits <= context.precision
|
2593
2783
|
expdiff = context.precision+1 - ans.number_of_digits
|
2594
|
-
ans = Decimal(ans.sign, Decimal.int_mult_radix_power(ans.
|
2784
|
+
ans = Decimal(ans.sign, Decimal.int_mult_radix_power(ans.coefficient, expdiff), ans.exponent-expdiff)
|
2595
2785
|
end
|
2596
2786
|
context.exception Underflow if ans.adjusted_exponent < context.emin
|
2597
2787
|
end
|
@@ -2621,13 +2811,13 @@ class Decimal
|
|
2621
2811
|
# log10(10**n) = n
|
2622
2812
|
if digits.first == 1 && digits[1..-1].all?{|d| d==0}
|
2623
2813
|
# answer may need rounding
|
2624
|
-
ans = Decimal(self.
|
2814
|
+
ans = Decimal(self.exponent + digits.size - 1)
|
2625
2815
|
return ans if context.exact?
|
2626
2816
|
else
|
2627
2817
|
# result is irrational, so necessarily inexact
|
2628
2818
|
return context.exception(Inexact, "Inexact power") if context.exact?
|
2629
|
-
c = self.
|
2630
|
-
e = self.
|
2819
|
+
c = self.coefficient
|
2820
|
+
e = self.exponent
|
2631
2821
|
p = context.precision
|
2632
2822
|
|
2633
2823
|
# correctly rounded result: repeatedly increase precision
|
@@ -2694,8 +2884,8 @@ class Decimal
|
|
2694
2884
|
ans = Decimal(+1, Decimal.int_radix_power(p+1)-1, -p-1)
|
2695
2885
|
else
|
2696
2886
|
# general case
|
2697
|
-
c = self.
|
2698
|
-
e = self.
|
2887
|
+
c = self.coefficient
|
2888
|
+
e = self.exponent
|
2699
2889
|
c = -c if self.sign == -1
|
2700
2890
|
|
2701
2891
|
# compute correctly rounded result: increase precision by
|
@@ -2744,8 +2934,8 @@ class Decimal
|
|
2744
2934
|
# result is irrational, so necessarily inexact
|
2745
2935
|
return context.exception(Inexact, 'Inexact exp') if context.exact?
|
2746
2936
|
|
2747
|
-
c = self.
|
2748
|
-
e = self.
|
2937
|
+
c = self.coefficient
|
2938
|
+
e = self.exponent
|
2749
2939
|
p = context.precision
|
2750
2940
|
|
2751
2941
|
# correctly rounded result: repeatedly increase precision by 3
|
@@ -2798,9 +2988,9 @@ class Decimal
|
|
2798
2988
|
|
2799
2989
|
return Decimal.new(self) if special?
|
2800
2990
|
return Decimal.new([sign, 0, exp]) if zero?
|
2801
|
-
return Decimal.new([sign, @coeff*Decimal.int_radix_power(self.
|
2802
|
-
#nd = number_of_digits + self.
|
2803
|
-
nd = exp - self.
|
2991
|
+
return Decimal.new([sign, @coeff*Decimal.int_radix_power(self.exponent - exp), exp]) if self.exponent > exp
|
2992
|
+
#nd = number_of_digits + self.exponent - exp
|
2993
|
+
nd = exp - self.exponent
|
2804
2994
|
if number_of_digits < nd
|
2805
2995
|
slf = Decimal.new([sign, 1, exp-1])
|
2806
2996
|
nd = number_of_digits
|
@@ -2817,7 +3007,7 @@ class Decimal
|
|
2817
3007
|
def _watched_rescale(exp, context, watch_exp)
|
2818
3008
|
if !watch_exp
|
2819
3009
|
ans = _rescale(exp, context.rounding)
|
2820
|
-
context.exception(Rounded) if ans.
|
3010
|
+
context.exception(Rounded) if ans.exponent > self.exponent
|
2821
3011
|
context.exception(Inexact) if ans != self
|
2822
3012
|
return ans
|
2823
3013
|
end
|
@@ -2835,7 +3025,7 @@ class Decimal
|
|
2835
3025
|
ans = _rescale(exp, context.rounding)
|
2836
3026
|
return context.exception(InvalidOperation,"exponent of rescale result too large for current context") if ans.adjusted_exponent > context.emax
|
2837
3027
|
return context.exception(InvalidOperation,"rescale result has too many digits for current context") if (ans.number_of_digits > context.precision) && !context.exact?
|
2838
|
-
if ans.
|
3028
|
+
if ans.exponent > self.exponent
|
2839
3029
|
context.exception(Rounded)
|
2840
3030
|
context.exception(Inexact) if ans!=self
|
2841
3031
|
end
|
@@ -2941,7 +3131,7 @@ class Decimal
|
|
2941
3131
|
d = Decimal.new(self)
|
2942
3132
|
end
|
2943
3133
|
changed = d._round(context.rounding, dg)
|
2944
|
-
coeff = Decimal.int_div_radix_power(d.
|
3134
|
+
coeff = Decimal.int_div_radix_power(d.coefficient, dg)
|
2945
3135
|
coeff += 1 if changed==1
|
2946
3136
|
ans = Decimal.new([sign, coeff, exp_min])
|
2947
3137
|
if changed!=0
|
@@ -2952,8 +3142,8 @@ class Decimal
|
|
2952
3142
|
context.exception Clamped
|
2953
3143
|
end
|
2954
3144
|
elsif ans.number_of_digits == context.precision+1
|
2955
|
-
if ans.
|
2956
|
-
ans = Decimal.new([ans.sign, Decimal.int_div_radix_power(ans.
|
3145
|
+
if ans.exponent< etop
|
3146
|
+
ans = Decimal.new([ans.sign, Decimal.int_div_radix_power(ans.coefficient,1), ans.exponent+1])
|
2957
3147
|
else
|
2958
3148
|
ans = context.exception(Overflow, 'above Emax', d.sign)
|
2959
3149
|
end
|
@@ -2994,9 +3184,9 @@ class Decimal
|
|
2994
3184
|
context = Decimal.define_context(context)
|
2995
3185
|
sign = self.sign * other.sign
|
2996
3186
|
if other.infinite?
|
2997
|
-
ideal_exp = self.
|
3187
|
+
ideal_exp = self.exponent
|
2998
3188
|
else
|
2999
|
-
ideal_exp = [self.
|
3189
|
+
ideal_exp = [self.exponent, other.exponent].min
|
3000
3190
|
end
|
3001
3191
|
|
3002
3192
|
expdiff = self.adjusted_exponent - other.adjusted_exponent
|
@@ -3004,9 +3194,9 @@ class Decimal
|
|
3004
3194
|
return [Decimal.new([sign, 0, 0]), _rescale(ideal_exp, context.rounding)]
|
3005
3195
|
end
|
3006
3196
|
if (expdiff <= context.precision) || context.exact?
|
3007
|
-
self_coeff = self.
|
3008
|
-
other_coeff = other.
|
3009
|
-
de = self.
|
3197
|
+
self_coeff = self.coefficient
|
3198
|
+
other_coeff = other.coefficient
|
3199
|
+
de = self.exponent - other.exponent
|
3010
3200
|
if de >= 0
|
3011
3201
|
self_coeff = Decimal.int_mult_radix_power(self_coeff, de)
|
3012
3202
|
else
|
@@ -3027,9 +3217,9 @@ class Decimal
|
|
3027
3217
|
context = Decimal.define_context(context)
|
3028
3218
|
sign = self.sign * other.sign
|
3029
3219
|
if other.infinite?
|
3030
|
-
ideal_exp = self.
|
3220
|
+
ideal_exp = self.exponent
|
3031
3221
|
else
|
3032
|
-
ideal_exp = [self.
|
3222
|
+
ideal_exp = [self.exponent, other.exponent].min
|
3033
3223
|
end
|
3034
3224
|
|
3035
3225
|
expdiff = self.adjusted_exponent - other.adjusted_exponent
|
@@ -3037,9 +3227,9 @@ class Decimal
|
|
3037
3227
|
return [Decimal.new([sign, 0, 0]), _rescale(ideal_exp, context.rounding)]
|
3038
3228
|
end
|
3039
3229
|
if (expdiff <= context.precision) || context.exact?
|
3040
|
-
self_coeff = self.
|
3041
|
-
other_coeff = other.
|
3042
|
-
de = self.
|
3230
|
+
self_coeff = self.coefficient*self.sign
|
3231
|
+
other_coeff = other.coefficient*other.sign
|
3232
|
+
de = self.exponent - other.exponent
|
3043
3233
|
if de >= 0
|
3044
3234
|
self_coeff = Decimal.int_mult_radix_power(self_coeff, de)
|
3045
3235
|
else
|
@@ -3108,12 +3298,12 @@ class Decimal
|
|
3108
3298
|
sign = other.even? ? +1 : -1
|
3109
3299
|
modulo = modulo.to_i.abs
|
3110
3300
|
|
3111
|
-
base = (self.
|
3301
|
+
base = (self.coefficient % modulo * (Decimal.int_radix_power(self.exponent) % modulo)) % modulo
|
3112
3302
|
|
3113
|
-
other.
|
3303
|
+
other.exponent.times do
|
3114
3304
|
base = (base**Decimal.radix) % modulo
|
3115
3305
|
end
|
3116
|
-
base = (base**other.
|
3306
|
+
base = (base**other.coefficient) % modulo
|
3117
3307
|
|
3118
3308
|
Decimal(sign, base, 0)
|
3119
3309
|
end
|
@@ -3127,8 +3317,8 @@ class Decimal
|
|
3127
3317
|
# Assumes that elimination of special cases has already been
|
3128
3318
|
# performed: self and other must both be nonspecial; self must
|
3129
3319
|
# be positive and not numerically equal to 1; other must be
|
3130
|
-
# nonzero. For efficiency, other.
|
3131
|
-
# so that 10**other.
|
3320
|
+
# nonzero. For efficiency, other.exponent should not be too large,
|
3321
|
+
# so that 10**other.exponent.abs is a feasible calculation.
|
3132
3322
|
def _power_exact(other, p)
|
3133
3323
|
|
3134
3324
|
# In the comments below, we write x for the value of self and
|
@@ -3176,15 +3366,15 @@ class Decimal
|
|
3176
3366
|
# Similarly, len(str(abs(yc)*xc_bits)) <= -ye implies |y|
|
3177
3367
|
# < 1/nbits(xc).
|
3178
3368
|
|
3179
|
-
xc = self.
|
3180
|
-
xe = self.
|
3369
|
+
xc = self.coefficient
|
3370
|
+
xe = self.exponent
|
3181
3371
|
while (xc % Decimal.radix) == 0
|
3182
3372
|
xc /= Decimal.radix
|
3183
3373
|
xe += 1
|
3184
3374
|
end
|
3185
3375
|
|
3186
|
-
yc = other.
|
3187
|
-
ye = other.
|
3376
|
+
yc = other.coefficient
|
3377
|
+
ye = other.exponent
|
3188
3378
|
while (yc % Decimal.radix) == 0
|
3189
3379
|
yc /= Decimal.radix
|
3190
3380
|
ye += 1
|
@@ -3202,7 +3392,7 @@ class Decimal
|
|
3202
3392
|
exponent = -exponent if other.sign == -1
|
3203
3393
|
# if other is a nonnegative integer, use ideal exponent
|
3204
3394
|
if other.integral? and (other.sign == +1)
|
3205
|
-
ideal_exponent = self.
|
3395
|
+
ideal_exponent = self.exponent*other.to_i
|
3206
3396
|
zeros = [exponent-ideal_exponent, p-1].min
|
3207
3397
|
else
|
3208
3398
|
zeros = 0
|
@@ -3320,7 +3510,7 @@ class Decimal
|
|
3320
3510
|
# exponent, if necessary
|
3321
3511
|
str_xc = xc.to_s
|
3322
3512
|
if other.integral? && other.sign == +1
|
3323
|
-
ideal_exponent = self.
|
3513
|
+
ideal_exponent = self.exponent*other.to_i
|
3324
3514
|
zeros = [xe-ideal_exponent, p-str_xc.length].min
|
3325
3515
|
else
|
3326
3516
|
zeros = 0
|
@@ -3448,12 +3638,12 @@ class Decimal
|
|
3448
3638
|
# 1-1/x <= log(x) <= x-1. If x > 1 we have |log10(x)| >
|
3449
3639
|
# (1-1/x)/2.31 > 0. If x < 1 then |log10(x)| > (1-x)/2.31 > 0
|
3450
3640
|
|
3451
|
-
adj = self.
|
3641
|
+
adj = self.exponent + number_of_digits - 1
|
3452
3642
|
return adj.to_s.length - 1 if adj >= 1 # self >= 10
|
3453
3643
|
return (-1-adj).to_s.length-1 if adj <= -2 # self < 0.1
|
3454
3644
|
|
3455
|
-
c = self.
|
3456
|
-
e = self.
|
3645
|
+
c = self.coefficient
|
3646
|
+
e = self.exponent
|
3457
3647
|
if adj == 0
|
3458
3648
|
# 1 < self < 10
|
3459
3649
|
num = (c - Decimal.int_radix_power(-e)).to_s
|
@@ -3470,7 +3660,7 @@ class Decimal
|
|
3470
3660
|
# that self is finite and positive and that self != 1.
|
3471
3661
|
def _ln_exp_bound
|
3472
3662
|
# for 0.1 <= x <= 10 we use the inequalities 1-1/x <= ln(x) <= x-1
|
3473
|
-
adj = self.
|
3663
|
+
adj = self.exponent + number_of_digits - 1
|
3474
3664
|
if adj >= 1
|
3475
3665
|
# argument >= 10; we use 23/10 = 2.3 as a lower bound for ln(10)
|
3476
3666
|
return (adj*23/10).to_s.length - 1
|
@@ -3479,8 +3669,8 @@ class Decimal
|
|
3479
3669
|
# argument <= 0.1
|
3480
3670
|
return ((-1-adj)*23/10).to_s.length - 1
|
3481
3671
|
end
|
3482
|
-
c = self.
|
3483
|
-
e = self.
|
3672
|
+
c = self.coefficient
|
3673
|
+
e = self.exponent
|
3484
3674
|
if adj == 0
|
3485
3675
|
# 1 < self < 10
|
3486
3676
|
num = (c-(10**-e)).to_s
|
@@ -3519,7 +3709,7 @@ class Decimal
|
|
3519
3709
|
|
3520
3710
|
# Normalizes op1, op2 to have the same exp and length of coefficient. Used for addition.
|
3521
3711
|
def _normalize(op1, op2, prec=0)
|
3522
|
-
if op1.
|
3712
|
+
if op1.exponent < op2.exponent
|
3523
3713
|
swap = true
|
3524
3714
|
tmp,other = op2,op1
|
3525
3715
|
else
|
@@ -3528,13 +3718,13 @@ class Decimal
|
|
3528
3718
|
end
|
3529
3719
|
tmp_len = tmp.number_of_digits
|
3530
3720
|
other_len = other.number_of_digits
|
3531
|
-
exp = tmp.
|
3532
|
-
if (other_len+other.
|
3721
|
+
exp = tmp.exponent + [-1, tmp_len - prec - 2].min
|
3722
|
+
if (other_len+other.exponent-1 < exp) && prec>0
|
3533
3723
|
other = Decimal.new([other.sign, 1, exp])
|
3534
3724
|
end
|
3535
3725
|
tmp = Decimal.new(tmp.sign,
|
3536
|
-
Decimal.int_mult_radix_power(tmp.
|
3537
|
-
other.
|
3726
|
+
Decimal.int_mult_radix_power(tmp.coefficient, tmp.exponent-other.exponent),
|
3727
|
+
other.exponent)
|
3538
3728
|
return swap ? [other, tmp] : [tmp, other]
|
3539
3729
|
end
|
3540
3730
|
|