bigdecimal 3.2.3-java → 3.3.0-java

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72ea230b182767a2c32bc9dafe2cb3b39165960ca8683b5afaab7dd9cfcae7fb
4
- data.tar.gz: f34cbd33b43a2ca0441d1f9b070fee1d27dc3ad9bba3e0df1f6774d688d18b68
3
+ metadata.gz: 7b3e1def7d1cfc2c72b5aa0a460ef768973e6c704a1225663e8df144718ac23a
4
+ data.tar.gz: 41e414cf6a1348e4567ce0b06928abb2c757a3a4f5de05ebcb1d78ff54140afe
5
5
  SHA512:
6
- metadata.gz: 55cb0f29873b8685d9cfeb14627fe9ecfed5e735a08cd82730da1d736d83b0f630f48e4ee0df164ba799ac0e54dbfbea629712b131d5e9355d07b4ed755edabb
7
- data.tar.gz: 8d0ac09e425579924920109a14290858b03bc70cd66ec3acae334593df0b786e922220a683f20bf2bd35900d1e5ebca1aa9cd3ed0463b20edc34ec3fbce2f01c
6
+ metadata.gz: c5880f8f1e8ed360116110107cf448894118a6714ecc916b3be74c056dab9e258b82620db42ace5a4b0b5833445859403e401fd2ba3a6c7d5aecaea1068986da
7
+ data.tar.gz: 33d209590eaa3132f31495884df9a5f08eae7382d349df1977c03b69d6e48c3a3bab0dee6fc5c99ead0690e5dee4f74364976d99381290b7c71f0ab4e93d7f07
@@ -7,6 +7,7 @@ require 'bigdecimal'
7
7
  # sqrt(x, prec)
8
8
  # sin (x, prec)
9
9
  # cos (x, prec)
10
+ # tan (x, prec)
10
11
  # atan(x, prec)
11
12
  # PI (prec)
12
13
  # E (prec) == exp(1.0,prec)
@@ -24,8 +25,8 @@ require 'bigdecimal'
24
25
  #
25
26
  # include BigMath
26
27
  #
27
- # a = BigDecimal((PI(100)/2).to_s)
28
- # puts sin(a,100) # => 0.99999999999999999999......e0
28
+ # a = BigDecimal((PI(49)/2).to_s)
29
+ # puts sin(a,100) # => 0.9999999999...9999999986e0
29
30
  #
30
31
  module BigMath
31
32
  module_function
@@ -36,13 +37,42 @@ module BigMath
36
37
  # Computes the square root of +decimal+ to the specified number of digits of
37
38
  # precision, +numeric+.
38
39
  #
39
- # BigMath.sqrt(BigDecimal('2'), 16).to_s
40
- # #=> "0.1414213562373095048801688724e1"
40
+ # BigMath.sqrt(BigDecimal('2'), 32).to_s
41
+ # #=> "0.14142135623730950488016887242097e1"
41
42
  #
42
43
  def sqrt(x, prec)
44
+ BigDecimal::Internal.validate_prec(prec, :sqrt)
45
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sqrt)
43
46
  x.sqrt(prec)
44
47
  end
45
48
 
49
+
50
+ # Returns [sign, reduced_x] where reduced_x is in -pi/2..pi/2
51
+ # and satisfies sin(x) = sign * sin(reduced_x)
52
+ # If add_half_pi is true, adds pi/2 to x before reduction.
53
+ # Precision of pi is adjusted to ensure reduced_x has the required precision.
54
+ private_class_method def _sin_periodic_reduction(x, prec, add_half_pi: false) # :nodoc:
55
+ return [1, x] if -Math::PI/2 <= x && x <= Math::PI/2 && !add_half_pi
56
+
57
+ mod_prec = prec + BigDecimal.double_fig
58
+ pi_extra_prec = [x.exponent, 0].max + BigDecimal.double_fig
59
+ while true
60
+ pi = PI(mod_prec + pi_extra_prec)
61
+ half_pi = pi / 2
62
+ div, mod = (add_half_pi ? x + pi : x + half_pi).divmod(pi)
63
+ mod -= half_pi
64
+ if mod.zero? || mod_prec + mod.exponent <= 0
65
+ # mod is too small to estimate required pi precision
66
+ mod_prec = mod_prec * 3 / 2 + BigDecimal.double_fig
67
+ elsif mod_prec + mod.exponent < prec
68
+ # Estimate required precision of pi
69
+ mod_prec = prec - mod.exponent + BigDecimal.double_fig
70
+ else
71
+ return [div % 2 == 0 ? 1 : -1, mod.mult(1, prec)]
72
+ end
73
+ end
74
+ end
75
+
46
76
  # call-seq:
47
77
  # sin(decimal, numeric) -> BigDecimal
48
78
  #
@@ -51,42 +81,33 @@ module BigMath
51
81
  #
52
82
  # If +decimal+ is Infinity or NaN, returns NaN.
53
83
  #
54
- # BigMath.sin(BigMath.PI(5)/4, 5).to_s
55
- # #=> "0.70710678118654752440082036563292800375e0"
84
+ # BigMath.sin(BigMath.PI(5)/4, 32).to_s
85
+ # #=> "0.70710807985947359435812921837984e0"
56
86
  #
57
87
  def sin(x, prec)
58
- raise ArgumentError, "Zero or negative precision for sin" if prec <= 0
59
- return BigDecimal("NaN") if x.infinite? || x.nan?
88
+ BigDecimal::Internal.validate_prec(prec, :sin)
89
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sin)
90
+ return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
60
91
  n = prec + BigDecimal.double_fig
61
92
  one = BigDecimal("1")
62
93
  two = BigDecimal("2")
63
- x = -x if neg = x < 0
64
- if x > 6
65
- twopi = two * BigMath.PI(prec + x.exponent)
66
- if x > 30
67
- x %= twopi
68
- else
69
- x -= twopi while x > twopi
70
- end
71
- end
94
+ sign, x = _sin_periodic_reduction(x, n)
72
95
  x1 = x
73
96
  x2 = x.mult(x,n)
74
- sign = 1
75
97
  y = x
76
98
  d = y
77
99
  i = one
78
100
  z = one
79
101
  while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
80
102
  m = BigDecimal.double_fig if m < BigDecimal.double_fig
81
- sign = -sign
82
- x1 = x2.mult(x1,n)
103
+ x1 = -x2.mult(x1,n)
83
104
  i += two
84
105
  z *= (i-one) * i
85
- d = sign * x1.div(z,m)
106
+ d = x1.div(z,m)
86
107
  y += d
87
108
  end
88
109
  y = BigDecimal("1") if y > 1
89
- neg ? -y : y
110
+ y.mult(sign, prec)
90
111
  end
91
112
 
92
113
  # call-seq:
@@ -97,41 +118,34 @@ module BigMath
97
118
  #
98
119
  # If +decimal+ is Infinity or NaN, returns NaN.
99
120
  #
100
- # BigMath.cos(BigMath.PI(4), 16).to_s
101
- # #=> "-0.999999999999999999999999999999856613163740061349e0"
121
+ # BigMath.cos(BigMath.PI(16), 32).to_s
122
+ # #=> "-0.99999999999999999999999999999997e0"
102
123
  #
103
124
  def cos(x, prec)
104
- raise ArgumentError, "Zero or negative precision for cos" if prec <= 0
105
- return BigDecimal("NaN") if x.infinite? || x.nan?
106
- n = prec + BigDecimal.double_fig
107
- one = BigDecimal("1")
108
- two = BigDecimal("2")
109
- x = -x if x < 0
110
- if x > 6
111
- twopi = two * BigMath.PI(prec + x.exponent)
112
- if x > 30
113
- x %= twopi
114
- else
115
- x -= twopi while x > twopi
116
- end
117
- end
118
- x1 = one
119
- x2 = x.mult(x,n)
120
- sign = 1
121
- y = one
122
- d = y
123
- i = BigDecimal("0")
124
- z = one
125
- while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
126
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
127
- sign = -sign
128
- x1 = x2.mult(x1,n)
129
- i += two
130
- z *= (i-one) * i
131
- d = sign * x1.div(z,m)
132
- y += d
133
- end
134
- y < -1 ? BigDecimal("-1") : y > 1 ? BigDecimal("1") : y
125
+ BigDecimal::Internal.validate_prec(prec, :cos)
126
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cos)
127
+ return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
128
+ sign, x = _sin_periodic_reduction(x, prec + BigDecimal.double_fig, add_half_pi: true)
129
+ sign * sin(x, prec)
130
+ end
131
+
132
+ # call-seq:
133
+ # tan(decimal, numeric) -> BigDecimal
134
+ #
135
+ # Computes the tangent of +decimal+ to the specified number of digits of
136
+ # precision, +numeric+.
137
+ #
138
+ # If +decimal+ is Infinity or NaN, returns NaN.
139
+ #
140
+ # BigMath.tan(BigDecimal("0.0"), 4).to_s
141
+ # #=> "0.0"
142
+ #
143
+ # BigMath.tan(BigMath.PI(24) / 4, 32).to_s
144
+ # #=> "0.99999999999999999999999830836025e0"
145
+ #
146
+ def tan(x, prec)
147
+ BigDecimal::Internal.validate_prec(prec, :tan)
148
+ sin(x, prec + BigDecimal.double_fig).div(cos(x, prec + BigDecimal.double_fig), prec)
135
149
  end
136
150
 
137
151
  # call-seq:
@@ -142,17 +156,18 @@ module BigMath
142
156
  #
143
157
  # If +decimal+ is NaN, returns NaN.
144
158
  #
145
- # BigMath.atan(BigDecimal('-1'), 16).to_s
146
- # #=> "-0.785398163397448309615660845819878471907514682065e0"
159
+ # BigMath.atan(BigDecimal('-1'), 32).to_s
160
+ # #=> "-0.78539816339744830961566084581988e0"
147
161
  #
148
162
  def atan(x, prec)
149
- raise ArgumentError, "Zero or negative precision for atan" if prec <= 0
150
- return BigDecimal("NaN") if x.nan?
151
- pi = PI(prec)
163
+ BigDecimal::Internal.validate_prec(prec, :atan)
164
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan)
165
+ return BigDecimal::Internal.nan_computation_result if x.nan?
166
+ n = prec + BigDecimal.double_fig
167
+ pi = PI(n)
152
168
  x = -x if neg = x < 0
153
169
  return pi.div(neg ? -2 : 2, prec) if x.infinite?
154
- return pi / (neg ? -4 : 4) if x.round(prec) == 1
155
- n = prec + BigDecimal.double_fig
170
+ return pi.div(neg ? -4 : 4, prec) if x.round(prec) == 1
156
171
  x = BigDecimal("1").div(x, n) if inv = x > 1
157
172
  x = (-1 + sqrt(1 + x.mult(x, n), n)).div(x, n) if dbl = x > 0.5
158
173
  y = x
@@ -170,7 +185,7 @@ module BigMath
170
185
  y *= 2 if dbl
171
186
  y = pi / 2 - y if inv
172
187
  y = -y if neg
173
- y
188
+ y.mult(1, prec)
174
189
  end
175
190
 
176
191
  # call-seq:
@@ -179,11 +194,11 @@ module BigMath
179
194
  # Computes the value of pi to the specified number of digits of precision,
180
195
  # +numeric+.
181
196
  #
182
- # BigMath.PI(10).to_s
183
- # #=> "0.3141592653589793238462643388813853786957412e1"
197
+ # BigMath.PI(32).to_s
198
+ # #=> "0.31415926535897932384626433832795e1"
184
199
  #
185
200
  def PI(prec)
186
- raise ArgumentError, "Zero or negative precision for PI" if prec <= 0
201
+ BigDecimal::Internal.validate_prec(prec, :PI)
187
202
  n = prec + BigDecimal.double_fig
188
203
  zero = BigDecimal("0")
189
204
  one = BigDecimal("1")
@@ -215,7 +230,7 @@ module BigMath
215
230
  pi = pi + d
216
231
  k = k+two
217
232
  end
218
- pi
233
+ pi.mult(1, prec)
219
234
  end
220
235
 
221
236
  # call-seq:
@@ -224,11 +239,11 @@ module BigMath
224
239
  # Computes e (the base of natural logarithms) to the specified number of
225
240
  # digits of precision, +numeric+.
226
241
  #
227
- # BigMath.E(10).to_s
228
- # #=> "0.271828182845904523536028752390026306410273e1"
242
+ # BigMath.E(32).to_s
243
+ # #=> "0.27182818284590452353602874713527e1"
229
244
  #
230
245
  def E(prec)
231
- raise ArgumentError, "Zero or negative precision for E" if prec <= 0
246
+ BigDecimal::Internal.validate_prec(prec, :E)
232
247
  BigMath.exp(1, prec)
233
248
  end
234
249
  end
@@ -119,8 +119,11 @@ class Rational < Numeric
119
119
  #
120
120
  # Returns the value as a BigDecimal.
121
121
  #
122
- # The required +precision+ parameter is used to determine the number of
123
- # significant digits for the result.
122
+ # The +precision+ parameter is used to determine the number of
123
+ # significant digits for the result. When +precision+ is set to +0+,
124
+ # the number of digits to represent the float being converted is determined
125
+ # automatically.
126
+ # The default +precision+ is +0+.
124
127
  #
125
128
  # require 'bigdecimal'
126
129
  # require 'bigdecimal/util'
@@ -129,7 +132,7 @@ class Rational < Numeric
129
132
  #
130
133
  # See also Kernel.BigDecimal.
131
134
  #
132
- def to_d(precision)
135
+ def to_d(precision=0)
133
136
  BigDecimal(self, precision)
134
137
  end
135
138
  end
@@ -141,29 +144,27 @@ class Complex < Numeric
141
144
  # cmp.to_d(precision) -> bigdecimal
142
145
  #
143
146
  # Returns the value as a BigDecimal.
147
+ # If the imaginary part is not +0+, an error is raised
144
148
  #
145
- # The +precision+ parameter is required for a rational complex number.
146
- # This parameter is used to determine the number of significant digits
147
- # for the result.
149
+ # The +precision+ parameter is used to determine the number of
150
+ # significant digits for the result. When +precision+ is set to +0+,
151
+ # the number of digits to represent the float being converted is determined
152
+ # automatically.
153
+ # The default +precision+ is +0+.
148
154
  #
149
155
  # require 'bigdecimal'
150
156
  # require 'bigdecimal/util'
151
157
  #
152
158
  # Complex(0.1234567, 0).to_d(4) # => 0.1235e0
153
159
  # Complex(Rational(22, 7), 0).to_d(3) # => 0.314e1
160
+ # Complex(1, 1).to_d # raises ArgumentError
154
161
  #
155
162
  # See also Kernel.BigDecimal.
156
163
  #
157
- def to_d(*args)
164
+ def to_d(precision=0)
158
165
  BigDecimal(self) unless self.imag.zero? # to raise error
159
166
 
160
- if args.length == 0
161
- case self.real
162
- when Rational
163
- BigDecimal(self.real) # to raise error
164
- end
165
- end
166
- self.real.to_d(*args)
167
+ BigDecimal(self.real, precision)
167
168
  end
168
169
  end
169
170
 
data/lib/bigdecimal.rb CHANGED
@@ -1,6 +1,11 @@
1
1
  if RUBY_ENGINE == 'jruby'
2
2
  JRuby::Util.load_ext("org.jruby.ext.bigdecimal.BigDecimalLibrary")
3
- return
3
+
4
+ class BigDecimal
5
+ def _decimal_shift(i) # :nodoc:
6
+ to_java.move_point_right(i).to_d
7
+ end
8
+ end
4
9
  else
5
10
  require 'bigdecimal.so'
6
11
  end
@@ -15,7 +20,7 @@ class BigDecimal
15
20
  when BigDecimal
16
21
  return x
17
22
  when Integer, Float
18
- return BigDecimal(x)
23
+ return BigDecimal(x, 0)
19
24
  when Rational
20
25
  return BigDecimal(x, [prec, 2 * BigDecimal.double_fig].max)
21
26
  end
@@ -160,22 +165,32 @@ class BigDecimal
160
165
  return BigDecimal(1).div(inv, prec)
161
166
  end
162
167
 
163
- int_part = y.fix.to_i
164
168
  prec2 = prec + BigDecimal.double_fig
165
- pow_prec = prec2 + (int_part > 0 ? y.exponent : 0)
166
- ans = BigDecimal(1)
167
- n = 1
168
- xn = x
169
- while true
170
- ans = ans.mult(xn, pow_prec) if int_part.allbits?(n)
171
- n <<= 1
172
- break if n > int_part
173
- xn = xn.mult(xn, pow_prec)
174
- end
175
- unless frac_part.zero?
176
- ans = ans.mult(BigMath.exp(BigMath.log(x, prec2).mult(frac_part, prec2), prec2), prec2)
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)
177
193
  end
178
- ans.mult(1, prec)
179
194
  end
180
195
 
181
196
  # Returns the square root of the value.
@@ -190,23 +205,19 @@ class BigDecimal
190
205
  raise FloatDomainError, "sqrt of 'NaN'(Not a Number)" if nan?
191
206
  return self if zero?
192
207
 
193
- limit = BigDecimal.limit.nonzero? if prec == 0
194
-
195
- # BigDecimal#sqrt calculates at least n_significant_digits precision.
196
- # This feature maybe problematic for some cases.
197
- n_digits = n_significant_digits
198
- prec = [prec, n_digits].max
208
+ if prec == 0
209
+ prec = BigDecimal.limit.nonzero? || n_significant_digits + BigDecimal.double_fig
210
+ end
199
211
 
200
212
  ex = exponent / 2
201
213
  x = _decimal_shift(-2 * ex)
202
- y = BigDecimal(Math.sqrt(x.to_f))
214
+ y = BigDecimal(Math.sqrt(x.to_f), 0)
203
215
  precs = [prec + BigDecimal.double_fig]
204
216
  precs << 2 + precs.last / 2 while precs.last > BigDecimal.double_fig
205
217
  precs.reverse_each do |p|
206
218
  y = y.add(x.div(y, p), p).div(2, p)
207
219
  end
208
- y = y.mult(1, limit) if limit
209
- y._decimal_shift(ex)
220
+ y._decimal_shift(ex).mult(1, prec)
210
221
  end
211
222
  end
212
223
 
@@ -232,56 +243,67 @@ module BigMath
232
243
 
233
244
  x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log)
234
245
  return BigDecimal::Internal.nan_computation_result if x.nan?
235
- raise Math::DomainError, 'Zero or negative argument for log' if x <= 0
246
+ raise Math::DomainError, 'Negative argument for log' if x < 0
247
+ return -BigDecimal::Internal.infinity_computation_result if x.zero?
236
248
  return BigDecimal::Internal.infinity_computation_result if x.infinite?
237
249
  return BigDecimal(0) if x == 1
238
250
 
251
+ prec2 = prec + BigDecimal.double_fig
239
252
  BigDecimal.save_limit do
240
253
  BigDecimal.limit(0)
241
254
  if x > 10 || x < 0.1
242
- log10 = log(BigDecimal(10), prec)
255
+ log10 = log(BigDecimal(10), prec2)
243
256
  exponent = x.exponent
244
257
  x = x._decimal_shift(-exponent)
245
258
  if x < 0.3
246
259
  x *= 10
247
260
  exponent -= 1
248
261
  end
249
- return log10 * exponent + log(x, prec)
262
+ return (log10 * exponent).add(log(x, prec2), prec)
250
263
  end
251
264
 
252
265
  x_minus_one_exponent = (x - 1).exponent
253
- prec += BigDecimal.double_fig
254
266
 
255
267
  # log(x) = log(sqrt(sqrt(sqrt(sqrt(x))))) * 2**sqrt_steps
256
- sqrt_steps = [Integer.sqrt(prec) + 3 * x_minus_one_exponent, 0].max
268
+ sqrt_steps = [Integer.sqrt(prec2) + 3 * x_minus_one_exponent, 0].max
257
269
 
258
270
  lg2 = 0.3010299956639812
259
- prec2 = prec + [-x_minus_one_exponent, 0].max + (sqrt_steps * lg2).ceil
271
+ sqrt_prec = prec2 + [-x_minus_one_exponent, 0].max + (sqrt_steps * lg2).ceil
260
272
 
261
273
  sqrt_steps.times do
262
- x = x.sqrt(prec2)
263
-
264
- # Workaround for https://github.com/ruby/bigdecimal/issues/354
265
- x = x.mult(1, prec2 + BigDecimal.double_fig)
274
+ x = x.sqrt(sqrt_prec)
266
275
  end
267
276
 
268
277
  # Taylor series for log(x) around 1
269
278
  # log(x) = -log((1 + X) / (1 - X)) where X = (x - 1) / (x + 1)
270
279
  # log(x) = 2 * (X + X**3 / 3 + X**5 / 5 + X**7 / 7 + ...)
271
- x = (x - 1).div(x + 1, prec2)
280
+ x = (x - 1).div(x + 1, sqrt_prec)
272
281
  y = x
273
- x2 = x.mult(x, prec)
282
+ x2 = x.mult(x, prec2)
274
283
  1.step do |i|
275
- n = prec + x.exponent - y.exponent + x2.exponent
284
+ n = prec2 + x.exponent - y.exponent + x2.exponent
276
285
  break if n <= 0 || x.zero?
277
286
  x = x.mult(x2.round(n - x2.exponent), n)
278
- y = y.add(x.div(2 * i + 1, n), prec)
287
+ y = y.add(x.div(2 * i + 1, n), prec2)
279
288
  end
280
289
 
281
290
  y.mult(2 ** (sqrt_steps + 1), prec)
282
291
  end
283
292
  end
284
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
+
285
307
  # call-seq:
286
308
  # BigMath.exp(decimal, numeric) -> BigDecimal
287
309
  #
@@ -298,23 +320,17 @@ module BigMath
298
320
  return BigDecimal::Internal.nan_computation_result if x.nan?
299
321
  return x.positive? ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0) if x.infinite?
300
322
  return BigDecimal(1) if x.zero?
301
- return BigDecimal(1).div(exp(-x, prec), prec) if x < 0
302
323
 
303
324
  # exp(x * 10**cnt) = exp(x)**(10**cnt)
304
- cnt = x > 1 ? x.exponent : 0
325
+ cnt = x < -1 || x > 1 ? x.exponent : 0
305
326
  prec2 = prec + BigDecimal.double_fig + cnt
306
327
  x = x._decimal_shift(-cnt)
307
- xn = BigDecimal(1)
308
- y = BigDecimal(1)
309
328
 
310
- # Taylor series for exp(x) around 0
311
- 1.step do |i|
312
- n = prec2 + xn.exponent
313
- break if n <= 0 || xn.zero?
314
- x = x.mult(1, n)
315
- xn = xn.mult(x, n).div(i, n)
316
- y = y.add(xn, prec2)
317
- end
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)
318
334
 
319
335
  # calculate exp(x * 10**cnt) from exp(x)
320
336
  # exp(x * 10**k) = exp(x * 10**(k - 1)) ** 10
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.3
4
+ version: 3.3.0
5
5
  platform: java
6
6
  authors:
7
7
  - Kenta Murata
@@ -9,7 +9,7 @@ authors:
9
9
  - Shigeo Kobayashi
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2025-09-03 00:00:00.000000000 Z
12
+ date: 2025-10-07 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: This library provides arbitrary-precision decimal floating-point number
15
15
  class.