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.
@@ -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