ruby-decimal 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +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
|
|