bigdecimal-pure 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/README.md +64 -14
- data/lib/bigdecimal/pure.rb +9 -319
- data/lib/bigdecimal_pure/calc.rb +582 -0
- data/lib/bigdecimal_pure/decimal.rb +1479 -0
- data/lib/bigdecimal_pure/math.rb +661 -0
- data/lib/bigdecimal_pure/util.rb +44 -0
- data/lib/bigdecimal_pure/version.rb +1 -1
- metadata +8 -3
|
@@ -0,0 +1,1479 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'version'
|
|
4
|
+
|
|
5
|
+
# BigDecimal in pure Ruby, modelled on bigdecimal 4.x.
|
|
6
|
+
#
|
|
7
|
+
# A finite value is a sign, an Integer coefficient without trailing zeros and
|
|
8
|
+
# a decimal exponent: sign * coef * 10**exp. NaN and the two infinities are
|
|
9
|
+
# kinds of their own, and zero keeps its sign as in the C version. Every
|
|
10
|
+
# operation computes the exact result and then rounds where the C version
|
|
11
|
+
# rounds, with the same rules for how many digits: + - * are exact unless
|
|
12
|
+
# BigDecimal.limit or a precision argument says otherwise, / keeps the larger
|
|
13
|
+
# operand's precision plus 16 digits (at least 32).
|
|
14
|
+
#
|
|
15
|
+
# The C version keeps the digits in 9-digit words. Where that shows through
|
|
16
|
+
# (the precision a Rational operand is converted with, the prefix of _dump,
|
|
17
|
+
# ROUND_UP to a position far left of the number) this class counts the words
|
|
18
|
+
# it would have used.
|
|
19
|
+
class BigDecimal < Numeric
|
|
20
|
+
BASE = 1_000_000_000
|
|
21
|
+
|
|
22
|
+
EXCEPTION_ALL = 0xff
|
|
23
|
+
EXCEPTION_NaN = 0x02 # rubocop:disable Naming/ConstantName
|
|
24
|
+
EXCEPTION_INFINITY = 0x01
|
|
25
|
+
EXCEPTION_UNDERFLOW = 0x04
|
|
26
|
+
EXCEPTION_OVERFLOW = 0x01 # the same bit as INFINITY, as in the C version
|
|
27
|
+
EXCEPTION_ZERODIVIDE = 0x10
|
|
28
|
+
|
|
29
|
+
ROUND_MODE = 0x100
|
|
30
|
+
ROUND_UP = 1
|
|
31
|
+
ROUND_DOWN = 2
|
|
32
|
+
ROUND_HALF_UP = 3
|
|
33
|
+
ROUND_HALF_DOWN = 4
|
|
34
|
+
ROUND_CEILING = 5
|
|
35
|
+
ROUND_FLOOR = 6
|
|
36
|
+
ROUND_HALF_EVEN = 7
|
|
37
|
+
|
|
38
|
+
SIGN_NaN = 0 # rubocop:disable Naming/ConstantName
|
|
39
|
+
SIGN_POSITIVE_ZERO = 1
|
|
40
|
+
SIGN_NEGATIVE_ZERO = -1
|
|
41
|
+
SIGN_POSITIVE_FINITE = 2
|
|
42
|
+
SIGN_NEGATIVE_FINITE = -2
|
|
43
|
+
SIGN_POSITIVE_INFINITE = 3
|
|
44
|
+
SIGN_NEGATIVE_INFINITE = -3
|
|
45
|
+
|
|
46
|
+
VERSION = BigDecimalPure::VERSION
|
|
47
|
+
PURE = true
|
|
48
|
+
|
|
49
|
+
DOUBLE_FIG = 16
|
|
50
|
+
ROUNDING_NAMES = {
|
|
51
|
+
up: ROUND_UP, down: ROUND_DOWN, truncate: ROUND_DOWN,
|
|
52
|
+
half_up: ROUND_HALF_UP, default: ROUND_HALF_UP, half_down: ROUND_HALF_DOWN,
|
|
53
|
+
half_even: ROUND_HALF_EVEN, banker: ROUND_HALF_EVEN,
|
|
54
|
+
ceiling: ROUND_CEILING, ceil: ROUND_CEILING, floor: ROUND_FLOOR
|
|
55
|
+
}.freeze
|
|
56
|
+
# The decimal exponent range of the C version: 9 * the word exponent range.
|
|
57
|
+
MAX_E10 = 9_223_372_036_854_775_800
|
|
58
|
+
MIN_E10 = -9_223_372_036_854_775_808
|
|
59
|
+
# Below this exponent no result can leave the range, so _make skips the check.
|
|
60
|
+
SAFE_EXP = 9_000_000_000_000_000_000
|
|
61
|
+
POW10 = Array.new(40) { |i| 10**i }.freeze
|
|
62
|
+
SPACE = [0x20, 0x09, 0x0a, 0x0b, 0x0c, 0x0d].freeze
|
|
63
|
+
SPECIAL_STRINGS = [['Infinity', :inf, 1], ['+Infinity', :inf, 1], ['-Infinity', :inf, -1], ['NaN', :nan, 0]].freeze
|
|
64
|
+
KEY_EXCEPTION = :__bigdecimal_pure_exception_mode__
|
|
65
|
+
KEY_ROUNDING = :__bigdecimal_pure_rounding_mode__
|
|
66
|
+
KEY_LIMIT = :__bigdecimal_pure_precision_limit__
|
|
67
|
+
private_constant :DOUBLE_FIG, :ROUNDING_NAMES, :MAX_E10, :MIN_E10, :SAFE_EXP, :POW10, :SPACE,
|
|
68
|
+
:SPECIAL_STRINGS, :KEY_EXCEPTION, :KEY_ROUNDING, :KEY_LIMIT
|
|
69
|
+
|
|
70
|
+
class << self
|
|
71
|
+
alias _alloc new
|
|
72
|
+
private :_alloc
|
|
73
|
+
undef_method :new
|
|
74
|
+
|
|
75
|
+
def allocate = raise(TypeError, 'allocator undefined for BigDecimal')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# ---- modes and limit, per thread (fiber) like the C version -------------
|
|
79
|
+
|
|
80
|
+
def self.mode(which, value = nil)
|
|
81
|
+
flag = _num2int(which)
|
|
82
|
+
if flag.anybits?(EXCEPTION_ALL)
|
|
83
|
+
current = _exception_mode
|
|
84
|
+
return current if value.nil?
|
|
85
|
+
raise ArgumentError, 'second argument must be true or false' unless [true, false].include?(value)
|
|
86
|
+
|
|
87
|
+
[EXCEPTION_INFINITY, EXCEPTION_NaN, EXCEPTION_UNDERFLOW, EXCEPTION_ZERODIVIDE].each do |bit|
|
|
88
|
+
current = value ? current | bit : current & ~bit if flag.anybits?(bit)
|
|
89
|
+
end
|
|
90
|
+
Thread.current[KEY_EXCEPTION] = current
|
|
91
|
+
return current
|
|
92
|
+
end
|
|
93
|
+
raise TypeError, 'first argument for BigDecimal.mode invalid' unless flag == ROUND_MODE
|
|
94
|
+
return _rounding_mode if value.nil?
|
|
95
|
+
|
|
96
|
+
Thread.current[KEY_ROUNDING] = _rounding_mode_of(value)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def self.limit(digits = nil)
|
|
100
|
+
current = _limit
|
|
101
|
+
unless digits.nil?
|
|
102
|
+
n = _num2int(digits)
|
|
103
|
+
raise ArgumentError, 'argument must be positive' if n.negative?
|
|
104
|
+
|
|
105
|
+
Thread.current[KEY_LIMIT] = n
|
|
106
|
+
end
|
|
107
|
+
current
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.save_exception_mode
|
|
111
|
+
saved = _exception_mode
|
|
112
|
+
begin
|
|
113
|
+
yield
|
|
114
|
+
ensure
|
|
115
|
+
Thread.current[KEY_EXCEPTION] = saved
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def self.save_rounding_mode
|
|
120
|
+
saved = _rounding_mode
|
|
121
|
+
begin
|
|
122
|
+
yield
|
|
123
|
+
ensure
|
|
124
|
+
Thread.current[KEY_ROUNDING] = saved
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def self.save_limit
|
|
129
|
+
saved = _limit
|
|
130
|
+
begin
|
|
131
|
+
yield
|
|
132
|
+
ensure
|
|
133
|
+
Thread.current[KEY_LIMIT] = saved
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def self.double_fig = DOUBLE_FIG
|
|
138
|
+
|
|
139
|
+
def self._exception_mode = Thread.current[KEY_EXCEPTION] || 0 # :nodoc:
|
|
140
|
+
def self._rounding_mode = Thread.current[KEY_ROUNDING] || ROUND_HALF_UP # :nodoc:
|
|
141
|
+
def self._limit = Thread.current[KEY_LIMIT] || 0 # :nodoc:
|
|
142
|
+
|
|
143
|
+
# ROUND_* as an Integer or one of its names; Strings are refused as in C.
|
|
144
|
+
def self._rounding_mode_of(value) # :nodoc:
|
|
145
|
+
if value.is_a?(Symbol)
|
|
146
|
+
ROUNDING_NAMES.fetch(value) { raise ArgumentError, "invalid rounding mode (#{value})" }
|
|
147
|
+
else
|
|
148
|
+
mode = _num2int(value)
|
|
149
|
+
raise ArgumentError, "invalid rounding mode (#{value})" unless (ROUND_UP..ROUND_HALF_EVEN).cover?(mode)
|
|
150
|
+
|
|
151
|
+
mode
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# round's half: option; nil means the current rounding mode.
|
|
156
|
+
def self._half_option(opts) # :nodoc:
|
|
157
|
+
mode = opts[:half]
|
|
158
|
+
return _rounding_mode if mode.nil?
|
|
159
|
+
|
|
160
|
+
text = if mode.is_a?(Symbol)
|
|
161
|
+
mode.to_s
|
|
162
|
+
else
|
|
163
|
+
(mode.respond_to?(:to_str) ? mode.to_str : nil)
|
|
164
|
+
end
|
|
165
|
+
case text&.downcase
|
|
166
|
+
when 'up' then ROUND_HALF_UP
|
|
167
|
+
when 'even' then ROUND_HALF_EVEN
|
|
168
|
+
when 'down' then ROUND_HALF_DOWN
|
|
169
|
+
else raise ArgumentError, "invalid rounding mode (#{text || mode})"
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# FloatDomainError when the exception mode has this bit.
|
|
174
|
+
def self._raise_if(bit, message) # :nodoc:
|
|
175
|
+
raise FloatDomainError, message if _exception_mode.anybits?(bit)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# ---- construction --------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
# A finite value; strips trailing zeros and keeps the exponent in range.
|
|
181
|
+
def self._make(sign, coef, exp) # :nodoc:
|
|
182
|
+
return sign.negative? ? N_ZERO : P_ZERO if coef.zero?
|
|
183
|
+
|
|
184
|
+
if (coef % 10).zero?
|
|
185
|
+
while (coef % 100_000_000).zero?
|
|
186
|
+
coef /= 100_000_000
|
|
187
|
+
exp += 8
|
|
188
|
+
end
|
|
189
|
+
while (coef % 10).zero?
|
|
190
|
+
coef /= 10
|
|
191
|
+
exp += 1
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
return _out_of_range(sign, coef, exp) if exp > SAFE_EXP || exp < -SAFE_EXP
|
|
195
|
+
|
|
196
|
+
_alloc(nil, sign, coef, exp)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def self._out_of_range(sign, coef, exp) # :nodoc:
|
|
200
|
+
e10 = exp + _ndigits(coef)
|
|
201
|
+
if e10 > MAX_E10
|
|
202
|
+
_raise_if(EXCEPTION_INFINITY, 'Exponent overflow')
|
|
203
|
+
sign.negative? ? N_INFINITY : INFINITY
|
|
204
|
+
elsif e10 < MIN_E10
|
|
205
|
+
_raise_if(EXCEPTION_UNDERFLOW, 'Exponent underflow')
|
|
206
|
+
sign.negative? ? N_ZERO : P_ZERO
|
|
207
|
+
else
|
|
208
|
+
_alloc(nil, sign, coef, exp)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def self._zero(sign) = sign.negative? ? N_ZERO : P_ZERO # :nodoc:
|
|
213
|
+
def self._infinity(sign) = sign.negative? ? N_INFINITY : INFINITY # :nodoc:
|
|
214
|
+
|
|
215
|
+
# A NaN of its own: the C version allocates one per result, and Array#include?
|
|
216
|
+
# and Hash look at identity before they call ==.
|
|
217
|
+
def self._nan = _alloc(:nan, 0, 0, 0) # :nodoc:
|
|
218
|
+
|
|
219
|
+
def self._ndigits(n) # :nodoc:
|
|
220
|
+
return n.to_s.length if n >= POW10[38]
|
|
221
|
+
|
|
222
|
+
d = (n.bit_length * 1233) >> 12
|
|
223
|
+
d += 1 if n >= POW10[d]
|
|
224
|
+
d.zero? ? 1 : d
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def self._pow10(n) = n < 40 ? POW10[n] : 10**n # :nodoc:
|
|
228
|
+
|
|
229
|
+
# Rounds sign*coef*10**exp to a multiple of 10**pos. sticky: something
|
|
230
|
+
# nonzero below coef's last digit was already dropped (inexact sqrt or
|
|
231
|
+
# division), which the directed and half modes must know about.
|
|
232
|
+
def self._round_to(sign, coef, exp, pos, mode, sticky: false) # :nodoc:
|
|
233
|
+
return _make(sign, coef, exp) if exp >= pos && !sticky
|
|
234
|
+
|
|
235
|
+
if exp >= pos
|
|
236
|
+
quotient = coef * _pow10(exp - pos)
|
|
237
|
+
rest = 0
|
|
238
|
+
half = nil
|
|
239
|
+
else
|
|
240
|
+
unit = _pow10(pos - exp)
|
|
241
|
+
quotient, rest = coef.divmod(unit)
|
|
242
|
+
half = unit / 2
|
|
243
|
+
end
|
|
244
|
+
inexact = rest.positive? || sticky
|
|
245
|
+
up = case mode
|
|
246
|
+
when ROUND_DOWN then false
|
|
247
|
+
when ROUND_UP then inexact
|
|
248
|
+
when ROUND_HALF_UP then half && rest >= half
|
|
249
|
+
when ROUND_HALF_DOWN then half && (rest > half || (rest == half && sticky))
|
|
250
|
+
when ROUND_HALF_EVEN then half && (rest > half || (rest == half && (sticky || quotient.odd?)))
|
|
251
|
+
when ROUND_CEILING then inexact && sign.positive?
|
|
252
|
+
when ROUND_FLOOR then inexact && sign.negative?
|
|
253
|
+
end
|
|
254
|
+
quotient += 1 if up
|
|
255
|
+
_make(sign, quotient, pos)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# To `digits` significant digits (VpLeftRound).
|
|
259
|
+
def self._round_sig(sign, coef, exp, digits, mode, sticky: false) # :nodoc:
|
|
260
|
+
return _zero(sign) if coef.zero?
|
|
261
|
+
|
|
262
|
+
_round_to(sign, coef, exp, exp + _ndigits(coef) - digits, mode, sticky: sticky)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# BigDecimal.limit applied to a result, or `digits` when that is given.
|
|
266
|
+
def self._limited(sign, coef, exp, digits = 0) = _rounded(sign, coef, exp, digits.zero? ? _limit : digits) # :nodoc:
|
|
267
|
+
|
|
268
|
+
# To `digits` significant digits in the rounding mode; 0 means exact.
|
|
269
|
+
def self._rounded(sign, coef, exp, digits) # :nodoc:
|
|
270
|
+
return _make(sign, coef, exp) if digits.zero? || coef.zero?
|
|
271
|
+
|
|
272
|
+
_round_sig(sign, coef, exp, digits, _rounding_mode)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def self._from_integer(n) = _make(n.negative? ? -1 : 1, n.abs, 0) # :nodoc:
|
|
276
|
+
|
|
277
|
+
# Float: its shortest decimal form cut to 16 digits (digits == 0), or
|
|
278
|
+
# rounded half-even to `digits` digits from the exact binary value.
|
|
279
|
+
def self._from_float(f, digits, exception: true) # :nodoc:
|
|
280
|
+
return _check(NAN) if f.nan?
|
|
281
|
+
return _check(f.positive? ? INFINITY : N_INFINITY) if f.infinite?
|
|
282
|
+
return (1 / f).negative? ? N_ZERO : P_ZERO if f.zero?
|
|
283
|
+
|
|
284
|
+
if digits > DOUBLE_FIG
|
|
285
|
+
return nil unless exception
|
|
286
|
+
|
|
287
|
+
raise ArgumentError, 'precision too large.'
|
|
288
|
+
end
|
|
289
|
+
sign = f.negative? ? -1 : 1
|
|
290
|
+
return _from_float_shortest(sign, f.abs) if digits.zero?
|
|
291
|
+
|
|
292
|
+
figures, k = _dtoa2(f.abs, digits)
|
|
293
|
+
_make(sign, figures.join.to_i, k + 1 - figures.size)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
TENS = Array.new(23) { |i| Float("1e#{i}") }.freeze
|
|
297
|
+
BIGTENS = [1e16, 1e32, 1e64, 1e128, 1e256].freeze
|
|
298
|
+
private_constant :TENS, :BIGTENS
|
|
299
|
+
|
|
300
|
+
# [digits, k]: the `ndigits` digits dtoa's mode 2 gives for d > 0, and
|
|
301
|
+
# d's decimal exponent (d is 0.digits * 10**(k+1)). The C version converts
|
|
302
|
+
# BigDecimal(float, n) with it, and it is not correctly rounded: a
|
|
303
|
+
# floating-point shortcut decides "about one half" within an error window
|
|
304
|
+
# and then rounds by the parity of the last digit. This repeats it step by
|
|
305
|
+
# step on the same doubles (missing/dtoa.c).
|
|
306
|
+
def self._dtoa2(d, ndigits) # :nodoc:
|
|
307
|
+
fraction, exponent = Math.frexp(d)
|
|
308
|
+
estimate = (((fraction * 2) - 1.5) * 0.289529654602168) + 0.1760912590558 + ((exponent - 1) * 0.301029995663981)
|
|
309
|
+
k = estimate.to_i
|
|
310
|
+
k -= 1 if estimate.negative? && estimate != k
|
|
311
|
+
k_check = true
|
|
312
|
+
if k.between?(0, 22)
|
|
313
|
+
k -= 1 if d < TENS[k]
|
|
314
|
+
k_check = false
|
|
315
|
+
end
|
|
316
|
+
half = false
|
|
317
|
+
if ndigits <= 14
|
|
318
|
+
quick = _dtoa2_quick(d, ndigits, k, k_check)
|
|
319
|
+
return quick unless quick == :half
|
|
320
|
+
|
|
321
|
+
half = true
|
|
322
|
+
end
|
|
323
|
+
return _dtoa2_integer(d, ndigits, k) if d == d.floor && k <= 14
|
|
324
|
+
|
|
325
|
+
_dtoa2_exact(d, ndigits, half)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def self._dtoa2_quick(d, ilim, k, k_check) # :nodoc: rubocop:disable Metrics
|
|
329
|
+
ieps = 2
|
|
330
|
+
if k.positive?
|
|
331
|
+
ds = TENS[k & 0xf]
|
|
332
|
+
j = k >> 4
|
|
333
|
+
if j.anybits?(0x10)
|
|
334
|
+
j &= 0x0f
|
|
335
|
+
d /= BIGTENS[4]
|
|
336
|
+
ieps += 1
|
|
337
|
+
end
|
|
338
|
+
i = 0
|
|
339
|
+
while j.positive?
|
|
340
|
+
if j.odd?
|
|
341
|
+
ieps += 1
|
|
342
|
+
ds *= BIGTENS[i]
|
|
343
|
+
end
|
|
344
|
+
j >>= 1
|
|
345
|
+
i += 1
|
|
346
|
+
end
|
|
347
|
+
d /= ds
|
|
348
|
+
elsif k.negative?
|
|
349
|
+
j = -k
|
|
350
|
+
d *= TENS[j & 0xf]
|
|
351
|
+
j >>= 4
|
|
352
|
+
i = 0
|
|
353
|
+
while j.positive?
|
|
354
|
+
if j.odd?
|
|
355
|
+
ieps += 1
|
|
356
|
+
d *= BIGTENS[i]
|
|
357
|
+
end
|
|
358
|
+
j >>= 1
|
|
359
|
+
i += 1
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
if k_check && d < 1.0
|
|
363
|
+
k -= 1
|
|
364
|
+
d *= 10.0
|
|
365
|
+
ieps += 1
|
|
366
|
+
end
|
|
367
|
+
eps = ((ieps * d) + 7.0) * (2.0**-52)
|
|
368
|
+
eps *= TENS[ilim - 1]
|
|
369
|
+
digits = []
|
|
370
|
+
i = 1
|
|
371
|
+
loop do
|
|
372
|
+
digit = d.to_i
|
|
373
|
+
d -= digit
|
|
374
|
+
ilim = i if d.zero?
|
|
375
|
+
digits << digit
|
|
376
|
+
if i == ilim
|
|
377
|
+
return _dtoa_bump(digits, k) if d > 0.5 + eps
|
|
378
|
+
return [_dtoa_strip(digits), k] if d < 0.5 - eps
|
|
379
|
+
return _dtoa_bump(digits, k) if digit.odd?
|
|
380
|
+
|
|
381
|
+
return :half
|
|
382
|
+
end
|
|
383
|
+
i += 1
|
|
384
|
+
d *= 10.0
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# An integer below 10**15, in doubles.
|
|
389
|
+
def self._dtoa2_integer(d, ilim, k) # :nodoc:
|
|
390
|
+
ds = TENS[k]
|
|
391
|
+
digits = []
|
|
392
|
+
i = 1
|
|
393
|
+
loop do
|
|
394
|
+
digit = (d / ds).to_i
|
|
395
|
+
d -= digit * ds
|
|
396
|
+
digits << digit
|
|
397
|
+
break if d.zero?
|
|
398
|
+
|
|
399
|
+
if i == ilim
|
|
400
|
+
d += d
|
|
401
|
+
return _dtoa_bump(digits, k) if d > ds || (d == ds && digit.odd?)
|
|
402
|
+
|
|
403
|
+
break
|
|
404
|
+
end
|
|
405
|
+
i += 1
|
|
406
|
+
d *= 10.0
|
|
407
|
+
end
|
|
408
|
+
[digits, k]
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# The exact digits; with `half` set by the shortcut, a round-up only
|
|
412
|
+
# happens on an odd last digit, and the nines it would carry through are
|
|
413
|
+
# dropped either way.
|
|
414
|
+
def self._dtoa2_exact(d, ilim, half) # :nodoc:
|
|
415
|
+
exact = d.to_r
|
|
416
|
+
k = Math.log10(d).floor
|
|
417
|
+
k -= 1 while exact < Rational(10)**k
|
|
418
|
+
k += 1 while exact >= Rational(10)**(k + 1)
|
|
419
|
+
scaled = exact * (Rational(10)**(ilim - 1 - k))
|
|
420
|
+
quotient = scaled.floor
|
|
421
|
+
rest = scaled - quotient
|
|
422
|
+
digits = quotient.digits.reverse
|
|
423
|
+
return [_dtoa_strip(digits), k] if rest.zero?
|
|
424
|
+
|
|
425
|
+
order = (rest * 2) <=> 1
|
|
426
|
+
return [_dtoa_strip(digits), k] unless order.positive? || (order.zero? && digits.last.odd?)
|
|
427
|
+
|
|
428
|
+
last = digits.size - 1
|
|
429
|
+
last -= 1 while last.positive? && digits[last] == 9
|
|
430
|
+
return [[1], k + 1] if digits[last] == 9
|
|
431
|
+
|
|
432
|
+
digits = digits[0..last]
|
|
433
|
+
digits[-1] += 1 if !half || digits[-1].odd?
|
|
434
|
+
[digits, k]
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
# dtoa's bump_up: one more in the last digit, carrying through nines.
|
|
438
|
+
def self._dtoa_bump(digits, k) # :nodoc:
|
|
439
|
+
last = digits.size - 1
|
|
440
|
+
last -= 1 while last.positive? && digits[last] == 9
|
|
441
|
+
return [[1], k + 1] if digits[last] == 9
|
|
442
|
+
|
|
443
|
+
[digits[0...last] + [digits[last] + 1], k]
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def self._dtoa_strip(digits) # :nodoc:
|
|
447
|
+
digits = digits.dup
|
|
448
|
+
digits.pop while digits.size > 1 && digits.last.zero?
|
|
449
|
+
digits
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def self._from_float_shortest(sign, f) # :nodoc:
|
|
453
|
+
mantissa, exponent = f.to_s.split('e')
|
|
454
|
+
whole, fraction = mantissa.split('.')
|
|
455
|
+
digits = whole + fraction.to_s
|
|
456
|
+
e10 = whole.length + exponent.to_i
|
|
457
|
+
stripped = digits.sub(/\A0+/, '')
|
|
458
|
+
e10 -= digits.length - stripped.length
|
|
459
|
+
digits = stripped.sub(/0+\z/, '')
|
|
460
|
+
digits = digits[0, DOUBLE_FIG] if digits.length > DOUBLE_FIG
|
|
461
|
+
_make(sign, digits.to_i, e10 - digits.length)
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# Rational: numerator / denominator to `digits` digits (0: as `/`).
|
|
465
|
+
def self._from_rational(r, digits) # :nodoc:
|
|
466
|
+
_from_integer(r.numerator).div(r.denominator, digits)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# Kernel#BigDecimal.
|
|
470
|
+
def self._convert(value, digits, exception) # :nodoc:
|
|
471
|
+
unless digits.nil?
|
|
472
|
+
digits = _to_int(digits)
|
|
473
|
+
if digits.negative?
|
|
474
|
+
return nil unless exception
|
|
475
|
+
|
|
476
|
+
raise ArgumentError, 'negative precision'
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
case value
|
|
480
|
+
when nil, true, false
|
|
481
|
+
return nil unless exception
|
|
482
|
+
|
|
483
|
+
raise TypeError, "can't convert #{value.inspect} into BigDecimal"
|
|
484
|
+
when BigDecimal then _check(value)
|
|
485
|
+
when Integer then _from_integer(value)
|
|
486
|
+
when Float then _from_float(value, digits || 0, exception: exception)
|
|
487
|
+
when Rational
|
|
488
|
+
if digits.nil?
|
|
489
|
+
return nil unless exception
|
|
490
|
+
|
|
491
|
+
raise ArgumentError, "can't omit precision for a Rational."
|
|
492
|
+
end
|
|
493
|
+
_from_rational(value, digits)
|
|
494
|
+
when Complex
|
|
495
|
+
unless _zero?(value.imaginary)
|
|
496
|
+
return nil unless exception
|
|
497
|
+
|
|
498
|
+
raise ArgumentError, 'Unable to make a BigDecimal from non-zero imaginary number'
|
|
499
|
+
end
|
|
500
|
+
_convert(value.real, digits, exception)
|
|
501
|
+
when String then _from_string(value, exception)
|
|
502
|
+
else
|
|
503
|
+
string = value.respond_to?(:to_str) ? value.to_str : nil
|
|
504
|
+
return _from_string(string, exception) if string.is_a?(String)
|
|
505
|
+
return nil unless exception
|
|
506
|
+
|
|
507
|
+
raise TypeError, "can't convert #{value.class} into BigDecimal"
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
def self._zero?(x)
|
|
512
|
+
case x
|
|
513
|
+
when Integer then x.zero?
|
|
514
|
+
when Rational then x.numerator.zero?
|
|
515
|
+
else x == 0 # rubocop:disable Style/NumericPredicate -- any Numeric, as rb_equal(x, 0)
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
private_class_method :_zero?
|
|
519
|
+
|
|
520
|
+
def self._from_string(string, exception) # :nodoc:
|
|
521
|
+
_ascii_compatible!(string)
|
|
522
|
+
if string.include?("\0")
|
|
523
|
+
return nil unless exception
|
|
524
|
+
|
|
525
|
+
raise ArgumentError, 'string contains null byte'
|
|
526
|
+
end
|
|
527
|
+
value = _parse(string, true)
|
|
528
|
+
if value.nil?
|
|
529
|
+
return nil unless exception
|
|
530
|
+
|
|
531
|
+
raise ArgumentError, "invalid value for BigDecimal(): \"#{string}\""
|
|
532
|
+
end
|
|
533
|
+
_check(value)
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
# String#to_d: reads as far as it can, 0 when nothing is a number.
|
|
537
|
+
def self.interpret_loosely(string)
|
|
538
|
+
string = _to_str(string)
|
|
539
|
+
_ascii_compatible!(string)
|
|
540
|
+
string = string[0, string.index("\0")] if string.include?("\0")
|
|
541
|
+
_check(_parse(string, false))
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
def self._ascii_compatible!(string)
|
|
545
|
+
return if string.encoding.ascii_compatible?
|
|
546
|
+
|
|
547
|
+
raise Encoding::CompatibilityError, "ASCII incompatible encoding: #{string.encoding}"
|
|
548
|
+
end
|
|
549
|
+
private_class_method :_ascii_compatible!
|
|
550
|
+
|
|
551
|
+
# The grammar of VpAlloc: spaces, Infinity/NaN, '#', sign, digits with
|
|
552
|
+
# single underscores between them, '.', digits, e/E/d/D with a signed
|
|
553
|
+
# exponent. strict (BigDecimal()) refuses anything else, nil for invalid;
|
|
554
|
+
# loose (String#to_d) stops at the first character it cannot use.
|
|
555
|
+
# Most strings are plain: sign, digits, a point, digits, an exponent. They
|
|
556
|
+
# read the same either way, so the byte scanner only sees the others.
|
|
557
|
+
SIMPLE_NUMBER = /\A[ \t\n\v\f\r]*([-+]?)(\d+)(?:\.(\d*))?(?:[eE]([-+]?\d+))?[ \t\n\v\f\r]*\z/
|
|
558
|
+
private_constant :SIMPLE_NUMBER
|
|
559
|
+
|
|
560
|
+
def self._parse(string, strict) # :nodoc:
|
|
561
|
+
if string.ascii_only? && (match = SIMPLE_NUMBER.match(string))
|
|
562
|
+
fraction = match[3] || ''
|
|
563
|
+
return _parsed(match[1] == '-' ? -1 : 1, match[2] + fraction, fraction.length, match[4].to_i)
|
|
564
|
+
end
|
|
565
|
+
_scan(string, strict)
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def self._scan(string, strict) # :nodoc: rubocop:disable Metrics
|
|
569
|
+
s = string.b
|
|
570
|
+
n = s.bytesize
|
|
571
|
+
i = 0
|
|
572
|
+
i += 1 while i < n && SPACE.include?(s.getbyte(i))
|
|
573
|
+
SPECIAL_STRINGS.each do |text, kind, sign|
|
|
574
|
+
next unless s.byteslice(i, text.bytesize) == text
|
|
575
|
+
|
|
576
|
+
j = i + text.bytesize
|
|
577
|
+
j += 1 while j < n && SPACE.include?(s.getbyte(j))
|
|
578
|
+
return kind == :nan ? _nan : _infinity(sign) if j == n
|
|
579
|
+
end
|
|
580
|
+
i += 1 if s.getbyte(i) == 0x23 # '#'
|
|
581
|
+
sign = 1
|
|
582
|
+
if [0x2b, 0x2d].include?(s.getbyte(i))
|
|
583
|
+
sign = -1 if s.getbyte(i) == 0x2d
|
|
584
|
+
i += 1
|
|
585
|
+
end
|
|
586
|
+
invalid = strict ? nil : P_ZERO
|
|
587
|
+
digit = ->(c) { c && c >= 0x30 && c <= 0x39 }
|
|
588
|
+
|
|
589
|
+
digits = +''
|
|
590
|
+
int_count = 0
|
|
591
|
+
stop = nil
|
|
592
|
+
loop do
|
|
593
|
+
c = s.getbyte(i)
|
|
594
|
+
break if c.nil? || (!strict && SPACE.include?(c))
|
|
595
|
+
|
|
596
|
+
if c == 0x5f # '_'
|
|
597
|
+
if int_count.positive?
|
|
598
|
+
after = s.getbyte(i + 1)
|
|
599
|
+
if after.nil? || SPACE.include?(after) || digit.call(after)
|
|
600
|
+
i += 1
|
|
601
|
+
next
|
|
602
|
+
end
|
|
603
|
+
break unless strict
|
|
604
|
+
end
|
|
605
|
+
return invalid
|
|
606
|
+
end
|
|
607
|
+
unless digit.call(c)
|
|
608
|
+
stop = c
|
|
609
|
+
break
|
|
610
|
+
end
|
|
611
|
+
digits << c
|
|
612
|
+
int_count += 1
|
|
613
|
+
i += 1
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
frac_count = 0
|
|
617
|
+
exp_count = 0
|
|
618
|
+
exp_seen = false
|
|
619
|
+
exp_digits = +''
|
|
620
|
+
exp_negative = false
|
|
621
|
+
if stop
|
|
622
|
+
if stop == 0x2e # '.'
|
|
623
|
+
i += 1
|
|
624
|
+
stop = nil
|
|
625
|
+
loop do
|
|
626
|
+
c = s.getbyte(i)
|
|
627
|
+
break if c.nil? || (!strict && SPACE.include?(c))
|
|
628
|
+
|
|
629
|
+
if c == 0x5f
|
|
630
|
+
if frac_count.positive? && digit.call(s.getbyte(i + 1))
|
|
631
|
+
i += 1
|
|
632
|
+
next
|
|
633
|
+
end
|
|
634
|
+
break unless strict
|
|
635
|
+
|
|
636
|
+
return nil
|
|
637
|
+
end
|
|
638
|
+
unless digit.call(c)
|
|
639
|
+
stop = c
|
|
640
|
+
break
|
|
641
|
+
end
|
|
642
|
+
digits << c
|
|
643
|
+
frac_count += 1
|
|
644
|
+
i += 1
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
if stop && [0x65, 0x45, 0x64, 0x44].include?(stop) # e E d D
|
|
648
|
+
exp_seen = true
|
|
649
|
+
i += 1
|
|
650
|
+
if [0x2b, 0x2d].include?(s.getbyte(i))
|
|
651
|
+
exp_negative = s.getbyte(i) == 0x2d
|
|
652
|
+
i += 1
|
|
653
|
+
end
|
|
654
|
+
stop = nil
|
|
655
|
+
loop do
|
|
656
|
+
c = s.getbyte(i)
|
|
657
|
+
break if c.nil? || (!strict && SPACE.include?(c))
|
|
658
|
+
|
|
659
|
+
if c == 0x5f
|
|
660
|
+
if exp_count.positive? && digit.call(s.getbyte(i + 1))
|
|
661
|
+
i += 1
|
|
662
|
+
next
|
|
663
|
+
end
|
|
664
|
+
unless strict
|
|
665
|
+
exp_seen = false if exp_count.zero?
|
|
666
|
+
break
|
|
667
|
+
end
|
|
668
|
+
return nil
|
|
669
|
+
end
|
|
670
|
+
unless digit.call(c)
|
|
671
|
+
stop = c
|
|
672
|
+
break
|
|
673
|
+
end
|
|
674
|
+
exp_digits << c
|
|
675
|
+
exp_count += 1
|
|
676
|
+
i += 1
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
if stop
|
|
680
|
+
i += 1 while i < n && SPACE.include?(s.getbyte(i))
|
|
681
|
+
return nil if strict && i < n
|
|
682
|
+
end
|
|
683
|
+
end
|
|
684
|
+
return nil if strict && ((int_count.zero? && frac_count.zero?) || (exp_seen && exp_count.zero?))
|
|
685
|
+
|
|
686
|
+
exponent = exp_digits.to_i
|
|
687
|
+
_parsed(sign, digits, frac_count, exp_negative ? -exponent : exponent)
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
# sign * digits (a String, frac_count of them after the point) * 10**exponent.
|
|
691
|
+
def self._parsed(sign, digits, frac_count, exponent) # :nodoc:
|
|
692
|
+
coef = digits.to_i
|
|
693
|
+
return _zero(sign) if coef.zero?
|
|
694
|
+
|
|
695
|
+
exp = exponent - frac_count
|
|
696
|
+
e10 = exp + _ndigits(coef)
|
|
697
|
+
if e10 > MAX_E10
|
|
698
|
+
_raise_if(EXCEPTION_INFINITY, 'exponent overflow')
|
|
699
|
+
return _infinity(sign)
|
|
700
|
+
end
|
|
701
|
+
return _zero(sign) if e10 < MIN_E10
|
|
702
|
+
|
|
703
|
+
_make(sign, coef, exp)
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
# CheckGetValue: a NaN or an infinity raises when its exception mode is on.
|
|
707
|
+
def self._check(value) # :nodoc:
|
|
708
|
+
return value if value.finite?
|
|
709
|
+
|
|
710
|
+
kind, sign = value._parts
|
|
711
|
+
if kind == :nan
|
|
712
|
+
_raise_if(EXCEPTION_NaN, "Computation results in 'NaN' (Not a Number)")
|
|
713
|
+
elsif kind == :inf
|
|
714
|
+
_raise_if(EXCEPTION_INFINITY, "Computation results in '#{'-' if sign.negative?}Infinity'")
|
|
715
|
+
end
|
|
716
|
+
value
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
# VpIsDefOP's result for an operation that met NaN or an infinity.
|
|
720
|
+
def self._special_result(value) # :nodoc:
|
|
721
|
+
return value if value.finite?
|
|
722
|
+
|
|
723
|
+
kind, sign = value._parts
|
|
724
|
+
if kind == :nan
|
|
725
|
+
_raise_if(EXCEPTION_NaN, "Computation results to 'NaN'")
|
|
726
|
+
elsif kind == :inf
|
|
727
|
+
_raise_if(EXCEPTION_INFINITY, "Computation results to '#{'-' if sign.negative?}Infinity'")
|
|
728
|
+
end
|
|
729
|
+
value
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
# NUM2INT: Integers, Floats truncated, anything with to_int.
|
|
733
|
+
def self._num2int(value) # :nodoc:
|
|
734
|
+
case value
|
|
735
|
+
when Integer then value
|
|
736
|
+
when Float
|
|
737
|
+
raise FloatDomainError, value.to_s unless value.finite?
|
|
738
|
+
|
|
739
|
+
value.truncate
|
|
740
|
+
when nil then raise TypeError, 'no implicit conversion from nil to integer'
|
|
741
|
+
else _to_int(value)
|
|
742
|
+
end
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
# rb_to_int.
|
|
746
|
+
def self._to_int(value) # :nodoc:
|
|
747
|
+
return value if value.is_a?(Integer)
|
|
748
|
+
return value.to_int if value.is_a?(Float) || (!value.nil? && value.respond_to?(:to_int))
|
|
749
|
+
|
|
750
|
+
raise TypeError, "no implicit conversion of #{_type_name(value)} into Integer"
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
def self._to_str(value) # :nodoc:
|
|
754
|
+
return value if value.is_a?(String)
|
|
755
|
+
return value.to_str if value.respond_to?(:to_str)
|
|
756
|
+
|
|
757
|
+
raise TypeError, "no implicit conversion of #{_type_name(value)} into String"
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
# How Ruby names a value in its conversion errors.
|
|
761
|
+
def self._type_name(value) # :nodoc:
|
|
762
|
+
case value
|
|
763
|
+
when nil, true, false then value.inspect
|
|
764
|
+
else value.class.to_s
|
|
765
|
+
end
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
# rb_special_const_p decides between inspect and the class in coerce errors.
|
|
769
|
+
def self._coerce_name(value) # :nodoc:
|
|
770
|
+
case value
|
|
771
|
+
when nil, true, false, Symbol, Integer, Float then value.inspect
|
|
772
|
+
else value.class.to_s
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
def self._load(string)
|
|
777
|
+
string = _to_str(string)
|
|
778
|
+
raise ArgumentError, 'string contains null byte' if string.include?("\0")
|
|
779
|
+
|
|
780
|
+
colon = string.index(':')
|
|
781
|
+
head = colon ? string[0, colon] : string
|
|
782
|
+
raise TypeError, 'load failed: invalid character in the marshaled string' unless head.match?(/\A\d*\z/)
|
|
783
|
+
|
|
784
|
+
rest = colon ? string[(colon + 1)..] : ''
|
|
785
|
+
value = _parse(rest, true)
|
|
786
|
+
raise ArgumentError, "invalid value for BigDecimal(): \"#{rest}\"" if value.nil?
|
|
787
|
+
|
|
788
|
+
_check(value)
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
# ---- instances -----------------------------------------------------------
|
|
792
|
+
|
|
793
|
+
def initialize(kind, sign, coef, exp) # rubocop:disable Lint/MissingSuper -- Numeric has nothing to set up
|
|
794
|
+
@kind = kind # nil (finite), :nan or :inf
|
|
795
|
+
@sign = sign
|
|
796
|
+
@coef = coef
|
|
797
|
+
@exp = exp
|
|
798
|
+
freeze
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
def _parts = [@kind, @sign, @coef, @exp] # :nodoc:
|
|
802
|
+
|
|
803
|
+
NAN = _alloc(:nan, 0, 0, 0)
|
|
804
|
+
INFINITY = _alloc(:inf, 1, 0, 0)
|
|
805
|
+
N_INFINITY = _alloc(:inf, -1, 0, 0)
|
|
806
|
+
P_ZERO = _alloc(nil, 1, 0, 0)
|
|
807
|
+
N_ZERO = _alloc(nil, -1, 0, 0)
|
|
808
|
+
private_constant :N_INFINITY, :P_ZERO, :N_ZERO
|
|
809
|
+
|
|
810
|
+
def nan? = @kind == :nan
|
|
811
|
+
def finite? = @kind.nil?
|
|
812
|
+
# infinite? and nonzero? answer nil, 1 or -1 and self or nil, as in C.
|
|
813
|
+
# rubocop:disable Naming/PredicateMethod, Style/ReturnNilInPredicateMethodDefinition
|
|
814
|
+
def infinite? = @kind == :inf ? @sign : nil
|
|
815
|
+
def zero? = @kind.nil? && @coef.zero?
|
|
816
|
+
def nonzero? = zero? ? nil : self
|
|
817
|
+
# rubocop:enable Naming/PredicateMethod, Style/ReturnNilInPredicateMethodDefinition
|
|
818
|
+
|
|
819
|
+
def sign
|
|
820
|
+
case @kind
|
|
821
|
+
when :nan then SIGN_NaN
|
|
822
|
+
when :inf then @sign * 3
|
|
823
|
+
else @coef.zero? ? @sign : @sign * 2
|
|
824
|
+
end
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
def exponent = @kind || @coef.zero? ? 0 : _e10
|
|
828
|
+
|
|
829
|
+
def precision
|
|
830
|
+
return 0 if @kind || @coef.zero?
|
|
831
|
+
|
|
832
|
+
e10 = _e10
|
|
833
|
+
(e10.positive? ? e10 : 0) - (@exp.negative? ? @exp : 0)
|
|
834
|
+
end
|
|
835
|
+
|
|
836
|
+
def scale = @kind || @coef.zero? || @exp >= 0 ? 0 : -@exp
|
|
837
|
+
def precision_scale = [precision, scale]
|
|
838
|
+
def n_significant_digits = @kind || @coef.zero? ? 0 : BigDecimal._ndigits(@coef)
|
|
839
|
+
|
|
840
|
+
def hash
|
|
841
|
+
return [BigDecimal, @kind, @sign].hash if @kind || @coef.zero?
|
|
842
|
+
|
|
843
|
+
[@sign, @coef, @exp].hash
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
def clone = self
|
|
847
|
+
def dup = self
|
|
848
|
+
def +@ = self
|
|
849
|
+
|
|
850
|
+
def -@
|
|
851
|
+
return BigDecimal._check(self) if @kind == :nan
|
|
852
|
+
return BigDecimal._check(BigDecimal._infinity(-@sign)) if @kind
|
|
853
|
+
|
|
854
|
+
BigDecimal._make(-@sign, @coef, @exp)
|
|
855
|
+
end
|
|
856
|
+
|
|
857
|
+
def abs
|
|
858
|
+
return BigDecimal._check(self) if @kind == :nan
|
|
859
|
+
return BigDecimal._check(INFINITY) if @kind
|
|
860
|
+
|
|
861
|
+
BigDecimal._make(1, @coef, @exp)
|
|
862
|
+
end
|
|
863
|
+
|
|
864
|
+
# ---- conversion ----------------------------------------------------------
|
|
865
|
+
|
|
866
|
+
def to_i
|
|
867
|
+
_raise_unless_finite
|
|
868
|
+
return 0 if _e10 <= 0
|
|
869
|
+
|
|
870
|
+
value = @exp >= 0 ? @coef * (10**@exp) : @coef / BigDecimal._pow10(-@exp)
|
|
871
|
+
raise FloatDomainError, 'Infinity' if value.is_a?(Float)
|
|
872
|
+
|
|
873
|
+
@sign.negative? ? -value : value
|
|
874
|
+
end
|
|
875
|
+
alias to_int to_i
|
|
876
|
+
|
|
877
|
+
def to_r
|
|
878
|
+
_raise_unless_finite
|
|
879
|
+
value = @exp >= 0 ? Rational(@coef * (10**@exp), 1) : Rational(@coef, BigDecimal._pow10(-@exp))
|
|
880
|
+
@sign.negative? ? -value : value
|
|
881
|
+
end
|
|
882
|
+
|
|
883
|
+
def to_f
|
|
884
|
+
case @kind
|
|
885
|
+
when :nan then return Float::NAN
|
|
886
|
+
when :inf then return @sign * Float::INFINITY
|
|
887
|
+
end
|
|
888
|
+
return @sign.negative? ? -0.0 : 0.0 if @coef.zero? # rubocop:disable Lint/DuplicateBranch, Style/IdenticalConditionalBranches -- -0.0 is not 0.0
|
|
889
|
+
|
|
890
|
+
words = 9 * _word_exponent
|
|
891
|
+
return _float_overflow if words > 317
|
|
892
|
+
return _float_underflow if words < -322
|
|
893
|
+
|
|
894
|
+
f = "#{'-' if @sign.negative?}0.#{@coef}e#{_e10}".to_f
|
|
895
|
+
return _float_underflow if f.zero?
|
|
896
|
+
return _float_overflow if f.infinite?
|
|
897
|
+
|
|
898
|
+
f
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
def to_s(*args)
|
|
902
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..1)" if args.size > 1
|
|
903
|
+
|
|
904
|
+
plain = false
|
|
905
|
+
plus = nil
|
|
906
|
+
group = 0
|
|
907
|
+
unless args.empty?
|
|
908
|
+
format = args[0]
|
|
909
|
+
if format.is_a?(String)
|
|
910
|
+
raise ArgumentError, 'string contains null byte' if format.include?("\0")
|
|
911
|
+
|
|
912
|
+
bytes = format.bytes
|
|
913
|
+
if [0x20, 0x2b].include?(bytes[0])
|
|
914
|
+
plus = bytes.shift == 0x20 ? ' ' : '+'
|
|
915
|
+
end
|
|
916
|
+
bytes.each do |c|
|
|
917
|
+
next if SPACE.include?(c)
|
|
918
|
+
|
|
919
|
+
unless c.between?(0x30, 0x39)
|
|
920
|
+
plain = [0x46, 0x66].include?(c)
|
|
921
|
+
break
|
|
922
|
+
end
|
|
923
|
+
group = (group * 10) + c - 0x30
|
|
924
|
+
end
|
|
925
|
+
else
|
|
926
|
+
group = BigDecimal._num2int(format)
|
|
927
|
+
raise ArgumentError, 'argument must be positive' unless group.positive?
|
|
928
|
+
end
|
|
929
|
+
end
|
|
930
|
+
special = _special_string(plus)
|
|
931
|
+
return special if special
|
|
932
|
+
|
|
933
|
+
prefix = @sign.negative? ? '-' : plus.to_s
|
|
934
|
+
plain ? prefix + _plain_string(group) : prefix + _exponent_string(group)
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
def inspect = to_s
|
|
938
|
+
|
|
939
|
+
def split
|
|
940
|
+
case @kind
|
|
941
|
+
when :nan then [0, 'NaN', 10, 0]
|
|
942
|
+
when :inf then [@sign, 'Infinity', 10, 0]
|
|
943
|
+
else [@sign, @coef.zero? ? '0' : @coef.to_s, 10, exponent]
|
|
944
|
+
end
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
def _dump(_level = nil) = "#{_words * 9}:#{self}"
|
|
948
|
+
|
|
949
|
+
def coerce(other)
|
|
950
|
+
value = _operand(other, 0)
|
|
951
|
+
raise TypeError, "#{BigDecimal._coerce_name(other)} can't be coerced into BigDecimal" if value.nil?
|
|
952
|
+
|
|
953
|
+
[BigDecimal._check(value), self]
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
# self * 10**n, as the C version's internal helper.
|
|
957
|
+
def _decimal_shift(n)
|
|
958
|
+
shift = BigDecimal._to_int(n)
|
|
959
|
+
return BigDecimal._check(self) if @kind || @coef.zero? || shift.zero?
|
|
960
|
+
|
|
961
|
+
BigDecimal._make(@sign, @coef, @exp + shift)
|
|
962
|
+
end
|
|
963
|
+
|
|
964
|
+
# ---- arithmetic ----------------------------------------------------------
|
|
965
|
+
|
|
966
|
+
def +(other) = _add(other, 1, 0, :+)
|
|
967
|
+
def -(other) = _add(other, -1, 0, :-)
|
|
968
|
+
def add(other, digits) = _add(other, 1, _precision_arg(digits), nil)
|
|
969
|
+
def sub(other, digits) = _add(other, -1, _precision_arg(digits), nil)
|
|
970
|
+
def *(other) = _mult(other, 0, :*)
|
|
971
|
+
def mult(other, digits) = _mult(other, _precision_arg(digits), nil)
|
|
972
|
+
def /(other) = _div(other, 0, :/)
|
|
973
|
+
|
|
974
|
+
def quo(value, *digits)
|
|
975
|
+
raise ArgumentError, "wrong number of arguments (given #{digits.size + 1}, expected 1..2)" if digits.size > 1
|
|
976
|
+
|
|
977
|
+
n = digits.empty? ? 0 : _precision_arg(digits[0])
|
|
978
|
+
n.positive? ? _div(value, n, nil) : _div(value, 0, :/)
|
|
979
|
+
end
|
|
980
|
+
|
|
981
|
+
# Without digits an Integer, rounded down, like Float#div; with digits a
|
|
982
|
+
# BigDecimal of that many digits, digits == 0 meaning as `/`.
|
|
983
|
+
def div(other, digits = nil)
|
|
984
|
+
if digits.nil?
|
|
985
|
+
quotient, = _divmod(other, false, :div)
|
|
986
|
+
return quotient.is_a?(BigDecimal) ? BigDecimal._check(quotient).to_i : quotient
|
|
987
|
+
end
|
|
988
|
+
_div(other, _precision_arg(digits), nil)
|
|
989
|
+
end
|
|
990
|
+
|
|
991
|
+
def %(other)
|
|
992
|
+
result = _divmod(other, false, :%)
|
|
993
|
+
result.is_a?(Array) ? BigDecimal._check(result[1]) : result
|
|
994
|
+
end
|
|
995
|
+
alias modulo %
|
|
996
|
+
|
|
997
|
+
def remainder(other)
|
|
998
|
+
result = _divmod(other, true, :remainder)
|
|
999
|
+
result.is_a?(Array) ? BigDecimal._check(result[1]) : result
|
|
1000
|
+
end
|
|
1001
|
+
|
|
1002
|
+
def divmod(other)
|
|
1003
|
+
result = _divmod(other, false, :divmod)
|
|
1004
|
+
return result unless result.is_a?(Array) && result[0].is_a?(BigDecimal)
|
|
1005
|
+
|
|
1006
|
+
[BigDecimal._check(result[0]).to_i, BigDecimal._check(result[1])]
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
# ---- rounding ------------------------------------------------------------
|
|
1010
|
+
|
|
1011
|
+
# round -> Integer; round(n) -> Integer for n < 1, else BigDecimal;
|
|
1012
|
+
# round(n, mode) and round(half: ...) -> BigDecimal.
|
|
1013
|
+
def round(*args)
|
|
1014
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..2)" if args.size > 2
|
|
1015
|
+
|
|
1016
|
+
mode = BigDecimal._rounding_mode
|
|
1017
|
+
position = 0
|
|
1018
|
+
integer = false
|
|
1019
|
+
case args.size
|
|
1020
|
+
when 0 then integer = true
|
|
1021
|
+
when 1
|
|
1022
|
+
if args[0].is_a?(Hash)
|
|
1023
|
+
mode = BigDecimal._half_option(args[0])
|
|
1024
|
+
else
|
|
1025
|
+
position = BigDecimal._num2int(args[0])
|
|
1026
|
+
integer = position < 1
|
|
1027
|
+
end
|
|
1028
|
+
when 2
|
|
1029
|
+
position = BigDecimal._num2int(args[0])
|
|
1030
|
+
mode = args[1].is_a?(Hash) ? BigDecimal._half_option(args[1]) : BigDecimal._rounding_mode_of(args[1])
|
|
1031
|
+
end
|
|
1032
|
+
rounded = BigDecimal._check(_round_at(position, mode))
|
|
1033
|
+
integer ? rounded.to_i : rounded
|
|
1034
|
+
end
|
|
1035
|
+
|
|
1036
|
+
def truncate(*args) = _round_method(args, ROUND_DOWN)
|
|
1037
|
+
def floor(*args) = _round_method(args, ROUND_FLOOR)
|
|
1038
|
+
def ceil(*args) = _round_method(args, ROUND_CEILING)
|
|
1039
|
+
def fix = BigDecimal._check(_round_at(0, ROUND_DOWN))
|
|
1040
|
+
|
|
1041
|
+
def frac
|
|
1042
|
+
return BigDecimal._check(self) if @kind || @coef.zero?
|
|
1043
|
+
return BigDecimal._zero(@sign) if @exp >= 0
|
|
1044
|
+
return self if _e10 <= 0
|
|
1045
|
+
|
|
1046
|
+
BigDecimal._make(@sign, @coef % BigDecimal._pow10(-@exp), @exp)
|
|
1047
|
+
end
|
|
1048
|
+
|
|
1049
|
+
# ---- comparison ----------------------------------------------------------
|
|
1050
|
+
|
|
1051
|
+
def <=>(other)
|
|
1052
|
+
return _compare_with(other, :<=>) unless other.is_a?(BigDecimal)
|
|
1053
|
+
|
|
1054
|
+
_compare(other)
|
|
1055
|
+
end
|
|
1056
|
+
|
|
1057
|
+
def ==(other)
|
|
1058
|
+
return _compare_with(other, :==) unless other.is_a?(BigDecimal)
|
|
1059
|
+
|
|
1060
|
+
_compare(other) == 0 # rubocop:disable Style/NumericPredicate -- nil for NaN
|
|
1061
|
+
end
|
|
1062
|
+
alias === ==
|
|
1063
|
+
alias eql? ==
|
|
1064
|
+
|
|
1065
|
+
def <(other)
|
|
1066
|
+
return _compare_with(other, :<) unless other.is_a?(BigDecimal)
|
|
1067
|
+
|
|
1068
|
+
(order = _compare(other)) ? order.negative? : false
|
|
1069
|
+
end
|
|
1070
|
+
|
|
1071
|
+
def <=(other)
|
|
1072
|
+
return _compare_with(other, :<=) unless other.is_a?(BigDecimal)
|
|
1073
|
+
|
|
1074
|
+
(order = _compare(other)) ? order <= 0 : false
|
|
1075
|
+
end
|
|
1076
|
+
|
|
1077
|
+
def >(other)
|
|
1078
|
+
return _compare_with(other, :>) unless other.is_a?(BigDecimal)
|
|
1079
|
+
|
|
1080
|
+
(order = _compare(other)) ? order.positive? : false
|
|
1081
|
+
end
|
|
1082
|
+
|
|
1083
|
+
def >=(other)
|
|
1084
|
+
return _compare_with(other, :>=) unless other.is_a?(BigDecimal)
|
|
1085
|
+
|
|
1086
|
+
(order = _compare(other)) ? order >= 0 : false
|
|
1087
|
+
end
|
|
1088
|
+
|
|
1089
|
+
protected
|
|
1090
|
+
|
|
1091
|
+
def _kind = @kind
|
|
1092
|
+
def _sign = @sign
|
|
1093
|
+
def _coef = @coef
|
|
1094
|
+
def _exp = @exp
|
|
1095
|
+
|
|
1096
|
+
# -1, 0, 1, or nil when either is NaN (VpComp).
|
|
1097
|
+
def _compare(other)
|
|
1098
|
+
kind = other._kind
|
|
1099
|
+
if @kind.nil? && kind.nil?
|
|
1100
|
+
cb = other._coef
|
|
1101
|
+
a = @coef.zero? ? 0 : @sign
|
|
1102
|
+
b = cb.zero? ? 0 : other._sign
|
|
1103
|
+
return a <=> b if a != b
|
|
1104
|
+
return 0 if a.zero?
|
|
1105
|
+
|
|
1106
|
+
magnitude = _magnitude_compare(cb, other._exp)
|
|
1107
|
+
return a.positive? ? magnitude : -magnitude
|
|
1108
|
+
end
|
|
1109
|
+
return nil if @kind == :nan || kind == :nan
|
|
1110
|
+
return kind ? @sign <=> other._sign : @sign if @kind
|
|
1111
|
+
|
|
1112
|
+
-other._sign
|
|
1113
|
+
end
|
|
1114
|
+
|
|
1115
|
+
# |self| <=> cb * 10**eb, both nonzero.
|
|
1116
|
+
def _magnitude_compare(cb, eb)
|
|
1117
|
+
return @coef <=> cb if @exp == eb
|
|
1118
|
+
|
|
1119
|
+
na = BigDecimal._ndigits(@coef)
|
|
1120
|
+
nb = BigDecimal._ndigits(cb)
|
|
1121
|
+
return (@exp + na) <=> (eb + nb) if @exp + na != eb + nb
|
|
1122
|
+
|
|
1123
|
+
if @exp > eb
|
|
1124
|
+
(@coef * BigDecimal._pow10(@exp - eb)) <=> cb
|
|
1125
|
+
else
|
|
1126
|
+
@coef <=> (cb * BigDecimal._pow10(eb - @exp))
|
|
1127
|
+
end
|
|
1128
|
+
end
|
|
1129
|
+
|
|
1130
|
+
# Decimal exponent: self is 0.ddd * 10**_e10.
|
|
1131
|
+
def _e10 = @exp + BigDecimal._ndigits(@coef)
|
|
1132
|
+
|
|
1133
|
+
# The C version's word exponent: 0.www * BASE**n.
|
|
1134
|
+
def _word_exponent = (_e10 + 8).div(9)
|
|
1135
|
+
|
|
1136
|
+
# How many 9-digit words the C version stores the digits in.
|
|
1137
|
+
def _words
|
|
1138
|
+
return 1 if @kind || @coef.zero?
|
|
1139
|
+
|
|
1140
|
+
(@exp + BigDecimal._ndigits(@coef) - 1).div(9) - @exp.div(9) + 1
|
|
1141
|
+
end
|
|
1142
|
+
|
|
1143
|
+
private
|
|
1144
|
+
|
|
1145
|
+
def _raise_unless_finite
|
|
1146
|
+
case @kind
|
|
1147
|
+
when :nan then raise FloatDomainError, "Computation results in 'NaN' (Not a Number)"
|
|
1148
|
+
when :inf then raise FloatDomainError, "Computation results in '#{'-' if @sign.negative?}Infinity'"
|
|
1149
|
+
end
|
|
1150
|
+
end
|
|
1151
|
+
|
|
1152
|
+
def _float_overflow
|
|
1153
|
+
BigDecimal._raise_if(EXCEPTION_OVERFLOW, 'BigDecimal to Float conversion')
|
|
1154
|
+
@sign.negative? ? -Float::INFINITY : Float::INFINITY
|
|
1155
|
+
end
|
|
1156
|
+
|
|
1157
|
+
def _float_underflow
|
|
1158
|
+
BigDecimal._raise_if(EXCEPTION_UNDERFLOW, 'BigDecimal to Float conversion')
|
|
1159
|
+
@sign.negative? ? -0.0 : 0.0 # rubocop:disable Lint/DuplicateBranch, Style/IdenticalConditionalBranches -- -0.0 is not 0.0
|
|
1160
|
+
end
|
|
1161
|
+
|
|
1162
|
+
def _special_string(plus)
|
|
1163
|
+
case @kind
|
|
1164
|
+
when :nan then 'NaN'
|
|
1165
|
+
when :inf then @sign.negative? ? '-Infinity' : "#{plus}Infinity"
|
|
1166
|
+
else
|
|
1167
|
+
return unless @coef.zero?
|
|
1168
|
+
|
|
1169
|
+
@sign.negative? ? '-0.0' : "#{plus}0.0"
|
|
1170
|
+
end
|
|
1171
|
+
end
|
|
1172
|
+
|
|
1173
|
+
def _exponent_string(group)
|
|
1174
|
+
digits = @coef.to_s
|
|
1175
|
+
digits = digits.scan(/.{1,#{group}}/).join(' ') if group.positive?
|
|
1176
|
+
"0.#{digits}e#{_e10}"
|
|
1177
|
+
end
|
|
1178
|
+
|
|
1179
|
+
def _plain_string(group)
|
|
1180
|
+
digits = @coef.to_s
|
|
1181
|
+
e10 = @exp + digits.length
|
|
1182
|
+
if @exp >= 0
|
|
1183
|
+
whole = digits + ('0' * @exp)
|
|
1184
|
+
fraction = ''
|
|
1185
|
+
elsif e10.positive?
|
|
1186
|
+
whole = digits[0, e10]
|
|
1187
|
+
fraction = digits[e10..]
|
|
1188
|
+
else
|
|
1189
|
+
whole = '0'
|
|
1190
|
+
fraction = ('0' * -e10) + digits
|
|
1191
|
+
end
|
|
1192
|
+
if group.positive?
|
|
1193
|
+
whole = whole.reverse.scan(/.{1,#{group}}/).join(' ').reverse if whole != '0'
|
|
1194
|
+
fraction = fraction.scan(/.{1,#{group}}/).join(' ')
|
|
1195
|
+
end
|
|
1196
|
+
"#{whole}.#{fraction.empty? ? '0' : fraction}"
|
|
1197
|
+
end
|
|
1198
|
+
|
|
1199
|
+
# check_int_precision: an Integer >= 0.
|
|
1200
|
+
def _precision_arg(digits)
|
|
1201
|
+
n = BigDecimal._num2int(digits)
|
|
1202
|
+
raise ArgumentError, 'negative precision' if n.negative?
|
|
1203
|
+
|
|
1204
|
+
n
|
|
1205
|
+
end
|
|
1206
|
+
|
|
1207
|
+
# GetCoercePrec: how many digits a Rational operand is converted with.
|
|
1208
|
+
def _coerce_prec(prec)
|
|
1209
|
+
prec = _words * 9 if prec.zero?
|
|
1210
|
+
[prec, 2 * DOUBLE_FIG].max
|
|
1211
|
+
end
|
|
1212
|
+
|
|
1213
|
+
# The other operand as a BigDecimal, or nil when it is not one of the four
|
|
1214
|
+
# types BigDecimal converts itself. prec is the operation's precision
|
|
1215
|
+
# argument, which decides a Rational's digits.
|
|
1216
|
+
def _operand(other, prec)
|
|
1217
|
+
case other
|
|
1218
|
+
when BigDecimal then other
|
|
1219
|
+
when Integer then BigDecimal._from_integer(other)
|
|
1220
|
+
when Float then BigDecimal._from_float(other, 0)
|
|
1221
|
+
when Rational then BigDecimal._from_rational(other, _coerce_prec(prec))
|
|
1222
|
+
end
|
|
1223
|
+
end
|
|
1224
|
+
|
|
1225
|
+
# rb_num_coerce_bin: other.coerce(self), then the operator on the pair.
|
|
1226
|
+
def _coerce_bin(other, operator)
|
|
1227
|
+
unless other.respond_to?(:coerce)
|
|
1228
|
+
raise TypeError, "#{BigDecimal._coerce_name(other)} can't be coerced into BigDecimal"
|
|
1229
|
+
end
|
|
1230
|
+
|
|
1231
|
+
pair = other.coerce(self)
|
|
1232
|
+
raise TypeError, 'coerce must return [x, y]' unless pair.is_a?(Array) && pair.size == 2
|
|
1233
|
+
|
|
1234
|
+
pair[0].public_send(operator, pair[1])
|
|
1235
|
+
end
|
|
1236
|
+
|
|
1237
|
+
def _operand!(other, prec, operator)
|
|
1238
|
+
value = _operand(other, prec)
|
|
1239
|
+
return value if value
|
|
1240
|
+
return nil if operator
|
|
1241
|
+
|
|
1242
|
+
raise TypeError, "#{BigDecimal._coerce_name(other)} can't be coerced into BigDecimal"
|
|
1243
|
+
end
|
|
1244
|
+
|
|
1245
|
+
def _add(other, op, prec, operator)
|
|
1246
|
+
b = _operand!(other, prec, operator)
|
|
1247
|
+
return _coerce_bin(other, operator) if b.nil?
|
|
1248
|
+
return BigDecimal._check(self) if @kind == :nan
|
|
1249
|
+
return BigDecimal._check(b) if b._kind == :nan
|
|
1250
|
+
|
|
1251
|
+
if @kind || b._kind
|
|
1252
|
+
result = if @kind && b._kind
|
|
1253
|
+
@sign == op * b._sign ? self : BigDecimal._nan
|
|
1254
|
+
elsif @kind
|
|
1255
|
+
self
|
|
1256
|
+
else
|
|
1257
|
+
BigDecimal._infinity(op * b._sign)
|
|
1258
|
+
end
|
|
1259
|
+
return BigDecimal._check(BigDecimal._special_result(result))
|
|
1260
|
+
end
|
|
1261
|
+
|
|
1262
|
+
_add_finite(b._coef, op * b._sign, b._exp, prec)
|
|
1263
|
+
end
|
|
1264
|
+
|
|
1265
|
+
def _add_finite(cb, sb, eb, prec)
|
|
1266
|
+
digits = prec.zero? ? BigDecimal._limit : prec
|
|
1267
|
+
ca = @coef
|
|
1268
|
+
sa = @sign
|
|
1269
|
+
ea = @exp
|
|
1270
|
+
if ca.zero?
|
|
1271
|
+
return BigDecimal._zero(sa.negative? && sb.negative? ? -1 : 1) if cb.zero?
|
|
1272
|
+
|
|
1273
|
+
return BigDecimal._rounded(sb, cb, eb, digits)
|
|
1274
|
+
end
|
|
1275
|
+
return BigDecimal._rounded(sa, ca, ea, digits) if cb.zero?
|
|
1276
|
+
|
|
1277
|
+
if digits.positive?
|
|
1278
|
+
# An operand far below the other's last digit and the rounding
|
|
1279
|
+
# position only decides the rounding: shrink it to one sticky digit.
|
|
1280
|
+
ha = ea + BigDecimal._ndigits(ca)
|
|
1281
|
+
hb = eb + BigDecimal._ndigits(cb)
|
|
1282
|
+
low_a = [ea, ha - 1 - digits].min
|
|
1283
|
+
low_b = [eb, hb - 1 - digits].min
|
|
1284
|
+
if hb <= low_a - 1
|
|
1285
|
+
cb = 1
|
|
1286
|
+
eb = low_a - 2
|
|
1287
|
+
elsif ha <= low_b - 1
|
|
1288
|
+
ca = 1
|
|
1289
|
+
ea = low_b - 2
|
|
1290
|
+
end
|
|
1291
|
+
end
|
|
1292
|
+
if ea < eb
|
|
1293
|
+
e = ea
|
|
1294
|
+
sum = (sa * ca) + (sb * cb * BigDecimal._pow10(eb - ea))
|
|
1295
|
+
else
|
|
1296
|
+
e = eb
|
|
1297
|
+
sum = (sa * ca * BigDecimal._pow10(ea - eb)) + (sb * cb)
|
|
1298
|
+
end
|
|
1299
|
+
return P_ZERO if sum.zero?
|
|
1300
|
+
|
|
1301
|
+
BigDecimal._rounded(sum.negative? ? -1 : 1, sum.abs, e, digits)
|
|
1302
|
+
end
|
|
1303
|
+
|
|
1304
|
+
def _mult(other, prec, operator)
|
|
1305
|
+
b = _operand!(other, prec, operator)
|
|
1306
|
+
return _coerce_bin(other, operator) if b.nil?
|
|
1307
|
+
|
|
1308
|
+
if @kind == :nan || b._kind == :nan
|
|
1309
|
+
return BigDecimal._check(BigDecimal._special_result(BigDecimal._nan))
|
|
1310
|
+
elsif @kind || b._kind
|
|
1311
|
+
zero = (@kind.nil? && @coef.zero?) || (b._kind.nil? && b._coef.zero?)
|
|
1312
|
+
result = zero ? BigDecimal._nan : BigDecimal._infinity(@sign * b._sign)
|
|
1313
|
+
return BigDecimal._check(BigDecimal._special_result(result))
|
|
1314
|
+
end
|
|
1315
|
+
|
|
1316
|
+
sign = @sign * b._sign
|
|
1317
|
+
return BigDecimal._zero(sign) if @coef.zero? || b._coef.zero?
|
|
1318
|
+
|
|
1319
|
+
coef = @coef * b._coef
|
|
1320
|
+
exp = @exp + b._exp
|
|
1321
|
+
return BigDecimal._round_sig(sign, coef, exp, prec, BigDecimal._rounding_mode) if prec.positive?
|
|
1322
|
+
|
|
1323
|
+
BigDecimal._limited(sign, coef, exp)
|
|
1324
|
+
end
|
|
1325
|
+
|
|
1326
|
+
def _div(other, digits, operator)
|
|
1327
|
+
b = _operand!(other, digits, operator)
|
|
1328
|
+
return _coerce_bin(other, operator) if b.nil?
|
|
1329
|
+
|
|
1330
|
+
if digits.zero?
|
|
1331
|
+
digits = [precision, b.precision].max + DOUBLE_FIG
|
|
1332
|
+
digits = 2 * DOUBLE_FIG if digits < 2 * DOUBLE_FIG
|
|
1333
|
+
limit = BigDecimal._limit
|
|
1334
|
+
digits = limit if limit.positive? && limit < digits
|
|
1335
|
+
end
|
|
1336
|
+
BigDecimal._check(_quotient(b, digits))
|
|
1337
|
+
end
|
|
1338
|
+
|
|
1339
|
+
# VpDivd plus the rounding of BigDecimal_div2.
|
|
1340
|
+
def _quotient(b, digits)
|
|
1341
|
+
sign = @sign * b._sign
|
|
1342
|
+
if @kind == :nan || b._kind == :nan
|
|
1343
|
+
return BigDecimal._special_result(BigDecimal._nan)
|
|
1344
|
+
elsif @kind
|
|
1345
|
+
return BigDecimal._special_result(b._kind ? BigDecimal._nan : BigDecimal._infinity(sign))
|
|
1346
|
+
elsif b._kind
|
|
1347
|
+
# The C version reports this zero as '-Infinity', whatever its sign.
|
|
1348
|
+
BigDecimal._raise_if(EXCEPTION_INFINITY, "Computation results to '-Infinity'")
|
|
1349
|
+
return BigDecimal._zero(sign)
|
|
1350
|
+
elsif b._coef.zero?
|
|
1351
|
+
if @coef.zero?
|
|
1352
|
+
BigDecimal._raise_if(EXCEPTION_NaN, "Computation results to 'NaN'")
|
|
1353
|
+
return BigDecimal._nan
|
|
1354
|
+
end
|
|
1355
|
+
BigDecimal._raise_if(EXCEPTION_ZERODIVIDE, 'Divide by zero')
|
|
1356
|
+
return BigDecimal._infinity(sign)
|
|
1357
|
+
end
|
|
1358
|
+
return BigDecimal._zero(sign) if @coef.zero?
|
|
1359
|
+
|
|
1360
|
+
# The C version divides into roomof(digits + 18, 9) words, from word
|
|
1361
|
+
# exponent a - b + 1 down, and rounds from those words alone; a
|
|
1362
|
+
# remainder only adds one to word roomof(digits, 9) when that word is
|
|
1363
|
+
# 0 or 500000000. When the first word comes out 0, a single digit may
|
|
1364
|
+
# remain past the rounding position, and a quotient just above a tie
|
|
1365
|
+
# rounds as the tie in the half modes. Kept, to round as C does.
|
|
1366
|
+
words = (digits + 26) / 9
|
|
1367
|
+
low = 9 * (_word_exponent - b._word_exponent + 1 - words)
|
|
1368
|
+
shift = @exp - b._exp - low
|
|
1369
|
+
quotient, rest = if shift >= 0
|
|
1370
|
+
(@coef * BigDecimal._pow10(shift)).divmod(b._coef)
|
|
1371
|
+
else
|
|
1372
|
+
@coef.divmod(b._coef * BigDecimal._pow10(-shift))
|
|
1373
|
+
end
|
|
1374
|
+
if rest.positive?
|
|
1375
|
+
below = ((BigDecimal._ndigits(quotient) + 8) / 9) - 1 - ((digits + 8) / 9)
|
|
1376
|
+
if below >= 0
|
|
1377
|
+
word = (quotient / BigDecimal._pow10(9 * below)) % BASE
|
|
1378
|
+
quotient += BigDecimal._pow10(9 * below) if word.zero? || word == BASE / 2
|
|
1379
|
+
else
|
|
1380
|
+
quotient = (quotient * BigDecimal._pow10(-9 * below)) + 1
|
|
1381
|
+
low += 9 * below
|
|
1382
|
+
end
|
|
1383
|
+
end
|
|
1384
|
+
BigDecimal._round_sig(sign, quotient, low, digits, BigDecimal._rounding_mode)
|
|
1385
|
+
end
|
|
1386
|
+
|
|
1387
|
+
# [div, mod] as BigDecimals (BigDecimal_DoDivmod), or what the coerced
|
|
1388
|
+
# operation returned when other is not a number BigDecimal converts.
|
|
1389
|
+
def _divmod(other, truncate, operator)
|
|
1390
|
+
b = _operand(other, 0)
|
|
1391
|
+
return _coerce_bin(other, operator) if b.nil?
|
|
1392
|
+
|
|
1393
|
+
return [NAN, NAN] if @kind == :nan || b._kind == :nan || (@kind && b._kind)
|
|
1394
|
+
raise ZeroDivisionError, 'divided by 0' if b._kind.nil? && b._coef.zero?
|
|
1395
|
+
return [BigDecimal._infinity(@sign * b._sign), NAN] if @kind
|
|
1396
|
+
return [P_ZERO, self] if @coef.zero?
|
|
1397
|
+
|
|
1398
|
+
if b._kind
|
|
1399
|
+
return [BigDecimal._from_integer(-1), b] if !truncate && (@sign * b._sign).negative?
|
|
1400
|
+
|
|
1401
|
+
return [P_ZERO, self]
|
|
1402
|
+
end
|
|
1403
|
+
e = [@exp, b._exp].min
|
|
1404
|
+
a = @sign * @coef * BigDecimal._pow10(@exp - e)
|
|
1405
|
+
d = b._sign * b._coef * BigDecimal._pow10(b._exp - e)
|
|
1406
|
+
quotient = a.abs / d.abs
|
|
1407
|
+
quotient = -quotient if a.negative? ^ d.negative?
|
|
1408
|
+
rest = a - (quotient * d)
|
|
1409
|
+
if !truncate && !rest.zero? && (a.negative? ^ d.negative?)
|
|
1410
|
+
quotient -= 1
|
|
1411
|
+
rest += d
|
|
1412
|
+
end
|
|
1413
|
+
[BigDecimal._from_integer(quotient), BigDecimal._make(rest.negative? ? -1 : 1, rest.abs, e)]
|
|
1414
|
+
end
|
|
1415
|
+
|
|
1416
|
+
# VpActiveRound: to `position` digits after the point (left for negative).
|
|
1417
|
+
def _round_at(position, mode)
|
|
1418
|
+
return self if @kind || @coef.zero?
|
|
1419
|
+
# The C version zeroes ROUND_UP when the position lies a whole word
|
|
1420
|
+
# left of the number, where it should give one unit.
|
|
1421
|
+
return BigDecimal._zero(@sign) if mode == ROUND_UP && (position + (9 * _word_exponent)).negative?
|
|
1422
|
+
|
|
1423
|
+
BigDecimal._round_to(@sign, @coef, @exp, -position, mode)
|
|
1424
|
+
end
|
|
1425
|
+
|
|
1426
|
+
def _round_method(args, mode)
|
|
1427
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..1)" if args.size > 1
|
|
1428
|
+
|
|
1429
|
+
rounded = _round_at(args.empty? ? 0 : BigDecimal._num2int(args[0]), mode)
|
|
1430
|
+
args.empty? ? BigDecimal._check(rounded).to_i : BigDecimal._check(rounded)
|
|
1431
|
+
end
|
|
1432
|
+
|
|
1433
|
+
def _compare_with(other, operator)
|
|
1434
|
+
b = _operand(other, 0)
|
|
1435
|
+
return _coerce_compare(other, operator) if b.nil?
|
|
1436
|
+
|
|
1437
|
+
order = _compare(b)
|
|
1438
|
+
return operator == :<=> ? nil : false if order.nil?
|
|
1439
|
+
|
|
1440
|
+
case operator
|
|
1441
|
+
when :<=> then order
|
|
1442
|
+
when :== then order.zero?
|
|
1443
|
+
when :< then order.negative?
|
|
1444
|
+
when :<= then order <= 0
|
|
1445
|
+
when :> then order.positive?
|
|
1446
|
+
when :>= then order >= 0
|
|
1447
|
+
end
|
|
1448
|
+
end
|
|
1449
|
+
|
|
1450
|
+
# rb_num_coerce_cmp for <=> and ==, rb_num_coerce_relop for the others.
|
|
1451
|
+
def _coerce_compare(other, operator)
|
|
1452
|
+
pair = other.respond_to?(:coerce) ? other.coerce(self) : nil
|
|
1453
|
+
unless pair.nil?
|
|
1454
|
+
raise TypeError, 'coerce must return [x, y]' unless pair.is_a?(Array) && pair.size == 2
|
|
1455
|
+
|
|
1456
|
+
result = pair[0].public_send(operator, pair[1])
|
|
1457
|
+
return result if operator == :<=>
|
|
1458
|
+
return result ? true : false if operator == :==
|
|
1459
|
+
return result unless result.nil?
|
|
1460
|
+
end
|
|
1461
|
+
return nil if operator == :<=>
|
|
1462
|
+
return false if operator == :==
|
|
1463
|
+
|
|
1464
|
+
raise ArgumentError, "comparison of BigDecimal with #{BigDecimal._coerce_name(other)} failed"
|
|
1465
|
+
end
|
|
1466
|
+
end
|
|
1467
|
+
|
|
1468
|
+
# Kernel#BigDecimal, the conversion function.
|
|
1469
|
+
module Kernel
|
|
1470
|
+
def BigDecimal(value, *digits, exception: true) # rubocop:disable Naming/MethodName
|
|
1471
|
+
unless exception == true || exception == false # rubocop:disable Style/MultipleComparison -- no Array per call
|
|
1472
|
+
raise ArgumentError, "expected true or false as exception: #{exception.inspect}"
|
|
1473
|
+
end
|
|
1474
|
+
raise ArgumentError, "wrong number of arguments (given #{digits.size + 1}, expected 1..2)" if digits.size > 1
|
|
1475
|
+
|
|
1476
|
+
BigDecimal._convert(value, digits.empty? ? nil : digits[0], exception)
|
|
1477
|
+
end
|
|
1478
|
+
module_function :BigDecimal
|
|
1479
|
+
end
|