bigdecimal 3.2.2 → 3.3.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.
data/lib/bigdecimal.rb CHANGED
@@ -1,5 +1,345 @@
1
1
  if RUBY_ENGINE == 'jruby'
2
2
  JRuby::Util.load_ext("org.jruby.ext.bigdecimal.BigDecimalLibrary")
3
+
4
+ class BigDecimal
5
+ def _decimal_shift(i) # :nodoc:
6
+ to_java.move_point_right(i).to_d
7
+ end
8
+ end
3
9
  else
4
10
  require 'bigdecimal.so'
5
11
  end
12
+
13
+ class BigDecimal
14
+ module Internal # :nodoc:
15
+
16
+ # Coerce x to BigDecimal with the specified precision.
17
+ # TODO: some methods (example: BigMath.exp) require more precision than specified to coerce.
18
+ def self.coerce_to_bigdecimal(x, prec, method_name) # :nodoc:
19
+ case x
20
+ when BigDecimal
21
+ return x
22
+ when Integer, Float
23
+ return BigDecimal(x, 0)
24
+ when Rational
25
+ return BigDecimal(x, [prec, 2 * BigDecimal.double_fig].max)
26
+ end
27
+ raise ArgumentError, "#{x.inspect} can't be coerced into BigDecimal"
28
+ end
29
+
30
+ def self.validate_prec(prec, method_name, accept_zero: false) # :nodoc:
31
+ raise ArgumentError, 'precision must be an Integer' unless Integer === prec
32
+ if accept_zero
33
+ raise ArgumentError, "Negative precision for #{method_name}" if prec < 0
34
+ else
35
+ raise ArgumentError, "Zero or negative precision for #{method_name}" if prec <= 0
36
+ end
37
+ end
38
+
39
+ def self.infinity_computation_result # :nodoc:
40
+ if BigDecimal.mode(BigDecimal::EXCEPTION_ALL).anybits?(BigDecimal::EXCEPTION_INFINITY)
41
+ raise FloatDomainError, "Computation results in 'Infinity'"
42
+ end
43
+ BigDecimal::INFINITY
44
+ end
45
+
46
+ def self.nan_computation_result # :nodoc:
47
+ if BigDecimal.mode(BigDecimal::EXCEPTION_ALL).anybits?(BigDecimal::EXCEPTION_NaN)
48
+ raise FloatDomainError, "Computation results to 'NaN'"
49
+ end
50
+ BigDecimal::NAN
51
+ end
52
+ end
53
+
54
+ # call-seq:
55
+ # self ** other -> bigdecimal
56
+ #
57
+ # Returns the \BigDecimal value of +self+ raised to power +other+:
58
+ #
59
+ # b = BigDecimal('3.14')
60
+ # b ** 2 # => 0.98596e1
61
+ # b ** 2.0 # => 0.98596e1
62
+ # b ** Rational(2, 1) # => 0.98596e1
63
+ #
64
+ # Related: BigDecimal#power.
65
+ #
66
+ def **(y)
67
+ case y
68
+ when BigDecimal, Integer, Float, Rational
69
+ power(y)
70
+ when nil
71
+ raise TypeError, 'wrong argument type NilClass'
72
+ else
73
+ x, y = y.coerce(self)
74
+ x**y
75
+ end
76
+ end
77
+
78
+ # call-seq:
79
+ # power(n)
80
+ # power(n, prec)
81
+ #
82
+ # Returns the value raised to the power of n.
83
+ #
84
+ # Also available as the operator **.
85
+ #
86
+ def power(y, prec = nil)
87
+ Internal.validate_prec(prec, :power) if prec
88
+ x = self
89
+ y = Internal.coerce_to_bigdecimal(y, prec || n_significant_digits, :power)
90
+
91
+ return Internal.nan_computation_result if x.nan? || y.nan?
92
+ return BigDecimal(1) if y.zero?
93
+
94
+ if y.infinite?
95
+ if x < 0
96
+ return BigDecimal(0) if x < -1 && y.negative?
97
+ return BigDecimal(0) if x > -1 && y.positive?
98
+ raise Math::DomainError, 'Result undefined for negative base raised to infinite power'
99
+ elsif x < 1
100
+ return y.positive? ? BigDecimal(0) : BigDecimal::Internal.infinity_computation_result
101
+ elsif x == 1
102
+ return BigDecimal(1)
103
+ else
104
+ return y.positive? ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0)
105
+ end
106
+ end
107
+
108
+ if x.infinite? && y < 0
109
+ # Computation result will be +0 or -0. Avoid overflow.
110
+ neg = x < 0 && y.frac.zero? && y % 2 == 1
111
+ return neg ? -BigDecimal(0) : BigDecimal(0)
112
+ end
113
+
114
+ if x.zero?
115
+ return BigDecimal(1) if y.zero?
116
+ return BigDecimal(0) if y > 0
117
+ if y.frac.zero? && y % 2 == 1 && x.sign == -1
118
+ return -BigDecimal::Internal.infinity_computation_result
119
+ else
120
+ return BigDecimal::Internal.infinity_computation_result
121
+ end
122
+ elsif x < 0
123
+ if y.frac.zero?
124
+ if y % 2 == 0
125
+ return (-x).power(y, prec)
126
+ else
127
+ return -(-x).power(y, prec)
128
+ end
129
+ else
130
+ raise Math::DomainError, 'Computation results in complex number'
131
+ end
132
+ elsif x == 1
133
+ return BigDecimal(1)
134
+ end
135
+
136
+ prec ||= BigDecimal.limit.nonzero?
137
+ frac_part = y.frac
138
+
139
+ if frac_part.zero? && !prec
140
+ # Infinite precision calculation for `x ** int` and `x.power(int)`
141
+ int_part = y.fix.to_i
142
+ int_part = -int_part if (neg = int_part < 0)
143
+ ans = BigDecimal(1)
144
+ n = 1
145
+ xn = x
146
+ while true
147
+ ans *= xn if int_part.allbits?(n)
148
+ n <<= 1
149
+ break if n > int_part
150
+ xn *= xn
151
+ # Detect overflow/underflow before consuming infinite memory
152
+ if (xn.exponent.abs - 1) * int_part / n >= 0x7FFFFFFFFFFFFFFF
153
+ return ((xn.exponent > 0) ^ neg ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0)) * (int_part.even? || x > 0 ? 1 : -1)
154
+ end
155
+ end
156
+ return neg ? BigDecimal(1) / ans : ans
157
+ end
158
+
159
+ prec ||= [x.n_significant_digits, y.n_significant_digits, BigDecimal.double_fig].max + BigDecimal.double_fig
160
+
161
+ if y < 0
162
+ inv = x.power(-y, prec)
163
+ return BigDecimal(0) if inv.infinite?
164
+ return BigDecimal::Internal.infinity_computation_result if inv.zero?
165
+ return BigDecimal(1).div(inv, prec)
166
+ end
167
+
168
+ prec2 = prec + BigDecimal.double_fig
169
+
170
+ if frac_part.zero? && y.exponent < Math.log(prec) * 5 + 20
171
+ # Use exponentiation by squaring if y is an integer and not too large
172
+ pow_prec = prec2 + y.exponent
173
+ n = 1
174
+ xn = x
175
+ ans = BigDecimal(1)
176
+ int_part = y.fix.to_i
177
+ while true
178
+ ans = ans.mult(xn, pow_prec) if int_part.allbits?(n)
179
+ n <<= 1
180
+ break if n > int_part
181
+ xn = xn.mult(xn, pow_prec)
182
+ end
183
+ ans.mult(1, prec)
184
+ else
185
+ if x > 1
186
+ # To calculate exp(z, prec), z needs prec+max(z.exponent, 0) precision if z > 0.
187
+ # Estimate (y*log(x)).exponent
188
+ logx_exponent = x < 2 ? (x - 1).exponent : Math.log10(x.exponent).round
189
+ ylogx_exponent = y.exponent + logx_exponent
190
+ prec2 += [ylogx_exponent, 0].max
191
+ end
192
+ BigMath.exp(BigMath.log(x, prec2).mult(y, prec2), prec)
193
+ end
194
+ end
195
+
196
+ # Returns the square root of the value.
197
+ #
198
+ # Result has at least prec significant digits.
199
+ #
200
+ def sqrt(prec)
201
+ Internal.validate_prec(prec, :sqrt, accept_zero: true)
202
+ return Internal.infinity_computation_result if infinite? == 1
203
+
204
+ raise FloatDomainError, 'sqrt of negative value' if self < 0
205
+ raise FloatDomainError, "sqrt of 'NaN'(Not a Number)" if nan?
206
+ return self if zero?
207
+
208
+ if prec == 0
209
+ prec = BigDecimal.limit.nonzero? || n_significant_digits + BigDecimal.double_fig
210
+ end
211
+
212
+ ex = exponent / 2
213
+ x = _decimal_shift(-2 * ex)
214
+ y = BigDecimal(Math.sqrt(x.to_f), 0)
215
+ precs = [prec + BigDecimal.double_fig]
216
+ precs << 2 + precs.last / 2 while precs.last > BigDecimal.double_fig
217
+ precs.reverse_each do |p|
218
+ y = y.add(x.div(y, p), p).div(2, p)
219
+ end
220
+ y._decimal_shift(ex).mult(1, prec)
221
+ end
222
+ end
223
+
224
+ # Core BigMath methods for BigDecimal (log, exp) are defined here.
225
+ # Other methods (sin, cos, atan) are defined in 'bigdecimal/math.rb'.
226
+ module BigMath
227
+
228
+ # call-seq:
229
+ # BigMath.log(decimal, numeric) -> BigDecimal
230
+ #
231
+ # Computes the natural logarithm of +decimal+ to the specified number of
232
+ # digits of precision, +numeric+.
233
+ #
234
+ # If +decimal+ is zero or negative, raises Math::DomainError.
235
+ #
236
+ # If +decimal+ is positive infinity, returns Infinity.
237
+ #
238
+ # If +decimal+ is NaN, returns NaN.
239
+ #
240
+ def self.log(x, prec)
241
+ BigDecimal::Internal.validate_prec(prec, :log)
242
+ raise Math::DomainError, 'Complex argument for BigMath.log' if Complex === x
243
+
244
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log)
245
+ return BigDecimal::Internal.nan_computation_result if x.nan?
246
+ raise Math::DomainError, 'Negative argument for log' if x < 0
247
+ return -BigDecimal::Internal.infinity_computation_result if x.zero?
248
+ return BigDecimal::Internal.infinity_computation_result if x.infinite?
249
+ return BigDecimal(0) if x == 1
250
+
251
+ prec2 = prec + BigDecimal.double_fig
252
+ BigDecimal.save_limit do
253
+ BigDecimal.limit(0)
254
+ if x > 10 || x < 0.1
255
+ log10 = log(BigDecimal(10), prec2)
256
+ exponent = x.exponent
257
+ x = x._decimal_shift(-exponent)
258
+ if x < 0.3
259
+ x *= 10
260
+ exponent -= 1
261
+ end
262
+ return (log10 * exponent).add(log(x, prec2), prec)
263
+ end
264
+
265
+ x_minus_one_exponent = (x - 1).exponent
266
+
267
+ # log(x) = log(sqrt(sqrt(sqrt(sqrt(x))))) * 2**sqrt_steps
268
+ sqrt_steps = [Integer.sqrt(prec2) + 3 * x_minus_one_exponent, 0].max
269
+
270
+ lg2 = 0.3010299956639812
271
+ sqrt_prec = prec2 + [-x_minus_one_exponent, 0].max + (sqrt_steps * lg2).ceil
272
+
273
+ sqrt_steps.times do
274
+ x = x.sqrt(sqrt_prec)
275
+ end
276
+
277
+ # Taylor series for log(x) around 1
278
+ # log(x) = -log((1 + X) / (1 - X)) where X = (x - 1) / (x + 1)
279
+ # log(x) = 2 * (X + X**3 / 3 + X**5 / 5 + X**7 / 7 + ...)
280
+ x = (x - 1).div(x + 1, sqrt_prec)
281
+ y = x
282
+ x2 = x.mult(x, prec2)
283
+ 1.step do |i|
284
+ n = prec2 + x.exponent - y.exponent + x2.exponent
285
+ break if n <= 0 || x.zero?
286
+ x = x.mult(x2.round(n - x2.exponent), n)
287
+ y = y.add(x.div(2 * i + 1, n), prec2)
288
+ end
289
+
290
+ y.mult(2 ** (sqrt_steps + 1), prec)
291
+ end
292
+ end
293
+
294
+ # Taylor series for exp(x) around 0
295
+ private_class_method def self._exp_taylor(x, prec) # :nodoc:
296
+ xn = BigDecimal(1)
297
+ y = BigDecimal(1)
298
+ 1.step do |i|
299
+ n = prec + xn.exponent
300
+ break if n <= 0 || xn.zero?
301
+ xn = xn.mult(x, n).div(i, n)
302
+ y = y.add(xn, prec)
303
+ end
304
+ y
305
+ end
306
+
307
+ # call-seq:
308
+ # BigMath.exp(decimal, numeric) -> BigDecimal
309
+ #
310
+ # Computes the value of e (the base of natural logarithms) raised to the
311
+ # power of +decimal+, to the specified number of digits of precision.
312
+ #
313
+ # If +decimal+ is infinity, returns Infinity.
314
+ #
315
+ # If +decimal+ is NaN, returns NaN.
316
+ #
317
+ def self.exp(x, prec)
318
+ BigDecimal::Internal.validate_prec(prec, :exp)
319
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :exp)
320
+ return BigDecimal::Internal.nan_computation_result if x.nan?
321
+ return x.positive? ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0) if x.infinite?
322
+ return BigDecimal(1) if x.zero?
323
+
324
+ # exp(x * 10**cnt) = exp(x)**(10**cnt)
325
+ cnt = x < -1 || x > 1 ? x.exponent : 0
326
+ prec2 = prec + BigDecimal.double_fig + cnt
327
+ x = x._decimal_shift(-cnt)
328
+
329
+ # Calculation of exp(small_prec) is fast because calculation of x**n is fast
330
+ # Calculation of exp(small_abs) converges fast.
331
+ # exp(x) = exp(small_prec_part + small_abs_part) = exp(small_prec_part) * exp(small_abs_part)
332
+ x_small_prec = x.round(Integer.sqrt(prec2))
333
+ y = _exp_taylor(x_small_prec, prec2).mult(_exp_taylor(x.sub(x_small_prec, prec2), prec2), prec2)
334
+
335
+ # calculate exp(x * 10**cnt) from exp(x)
336
+ # exp(x * 10**k) = exp(x * 10**(k - 1)) ** 10
337
+ cnt.times do
338
+ y2 = y.mult(y, prec2)
339
+ y5 = y2.mult(y2, prec2).mult(y, prec2)
340
+ y = y5.mult(y5, prec2)
341
+ end
342
+
343
+ y.mult(1, prec)
344
+ end
345
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bigdecimal
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.2
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenta Murata