Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions lib/bigdecimal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,18 @@ def self.newton_loop(prec, initial_precision: BigDecimal.double_fig / 2, safe_ma
end
end

# Fast and rough conversion to float for mathematical calculations.
# Bigdecimal#to_f is slow when n_significant_digits is large.
# This is because to_f internally converts BigDecimal to String
# to get the exact nearest float representation.
# TODO: Remove this workaround when BigDecimal#to_f is optimized.
def self.fast_to_f(x) # :nodoc:
x.n_significant_digits < 40 ? x.to_f : x.mult(1, 20).to_f
end

# Calculates Math.log(x.to_f) considering large or small exponent
def self.float_log(x) # :nodoc:
Math.log(x._decimal_shift(-x.exponent).to_f) + x.exponent * Math.log(10)
Math.log(fast_to_f(x._decimal_shift(-x.exponent))) + x.exponent * Math.log(10)
end

# Calculating Taylor series sum using binary splitting method
Expand Down Expand Up @@ -268,7 +277,7 @@ def sqrt(prec)

ex = exponent / 2
x = _decimal_shift(-2 * ex)
y = BigDecimal(Math.sqrt(x.to_f), 0)
y = BigDecimal(Math.sqrt(BigDecimal::Internal.fast_to_f(x)), 0)
Internal.newton_loop(prec + BigDecimal::Internal::EXTRA_PREC) do |p|
y = y.add(x.div(y, p), p).div(2, p)
end
Expand Down
10 changes: 5 additions & 5 deletions lib/bigdecimal/math.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def cbrt(x, prec)
x = -x if neg = x < 0
ex = x.exponent / 3
x = x._decimal_shift(-3 * ex)
y = BigDecimal(Math.cbrt(x.to_f), 0)
y = BigDecimal(Math.cbrt(BigDecimal::Internal.fast_to_f(x)), 0)
BigDecimal::Internal.newton_loop(prec + BigDecimal::Internal::EXTRA_PREC) do |p|
y = (2 * y + x.div(y, p).div(y, p)).div(3, p)
end
Expand Down Expand Up @@ -304,7 +304,7 @@ def atan(x, prec)

# Solve tan(y) - x = 0 with Newton's method
# Repeat: y -= (tan(y) - x) * cos(y)**2
y = BigDecimal(Math.atan(x.to_f), 0)
y = BigDecimal(Math.atan(BigDecimal::Internal.fast_to_f(x)), 0)
BigDecimal::Internal.newton_loop(n) do |p|
s = sin(y, p)
c = (1 - s * s).sqrt(p)
Expand Down Expand Up @@ -605,7 +605,7 @@ def erf(x, prec)
return BigDecimal(1) if x > 5000000000 # erf(5000000000) > 1 - 1e-10000000000000000000

if x > 8
xf = x.to_f
xf = BigDecimal::Internal.fast_to_f(x)
log10_erfc = -xf ** 2 / Math.log(10) - Math.log10(xf * Math::PI ** 0.5)
erfc_prec = [prec + log10_erfc.ceil, 1].max
erfc = _erfc_asymptotic(x, erfc_prec)
Expand Down Expand Up @@ -647,7 +647,7 @@ def erfc(x, prec)
# erfc(x) = 1 - erf(x) < exp(-x**2)/x/sqrt(pi)
# Precision of erf(x) needs about log10(exp(-x**2)/x/sqrt(pi)) extra digits
log10 = 2.302585092994046
xf = x.to_f
xf = BigDecimal::Internal.fast_to_f(x)
high_prec = prec + BigDecimal::Internal::EXTRA_PREC + ((xf**2 + Math.log(xf) + Math.log(Math::PI)/2) / log10).ceil
BigDecimal(1).sub(erf(x, high_prec), prec)
end
Expand Down Expand Up @@ -698,7 +698,7 @@ def erfc(x, prec)
# sqrt(2)/2 + k*log(k) - k - 2*k*log(x) < -prec*log(10)
# and the left side is minimized when k = x**2.
prec += BigDecimal::Internal::EXTRA_PREC
xf = x.to_f
xf = BigDecimal::Internal.fast_to_f(x)
kmax = (1..(xf ** 2).floor).bsearch do |k|
Math.log(2) / 2 + k * Math.log(k) - k - 2 * k * Math.log(xf) < -prec * Math.log(10)
end
Expand Down