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
47 changes: 34 additions & 13 deletions lib/bigdecimal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,29 +46,36 @@ def power(y, prec = nil)
x = self
y = BigMath._coerce_to_bigdecimal(y, :power)

return BigDecimal::NAN if x.nan? || y.nan?
return BigMath._nan_computation_result if x.nan? || y.nan?
return BigDecimal(1) if y.zero?

if y.infinite?
if x < 0
return BigDecimal(0) if x < -1 && y.negative?
return BigDecimal(0) if x > -1 && y.positive?
raise Math::DomainError, 'Result undefined for negative base raised to infinite power'
elsif x < 1
return y.positive? ? BigDecimal(0) : BigDecimal::INFINITY
return y.positive? ? BigDecimal(0) : BigMath._infinity_computation_result
elsif x == 1
return BigDecimal(1)
else
return y.positive? ? BigDecimal::INFINITY : BigDecimal(0)
return y.positive? ? BigMath._infinity_computation_result : BigDecimal(0)
end
end

if x.infinite? && y < 0
# Computation result will be +0 or -0. Avoid overflow.
neg = x < 0 && y.frac.zero? && y % 2 == 1
return neg ? -BigDecimal(0) : BigDecimal(0)
end

if x.zero?
return BigDecimal(1) if y.zero?
return BigDecimal(0) if y > 0
if y.frac.zero? && y % 2 == 1 && x.sign == -1
return -BigDecimal::INFINITY
return -BigMath._infinity_computation_result
else
return BigDecimal::INFINITY
return BigMath._infinity_computation_result
end
elsif x < 0
if y.frac.zero?
Expand Down Expand Up @@ -101,7 +108,7 @@ def power(y, prec = nil)
xn *= xn
# Detect overflow/underflow before consuming infinite memory
if (xn.exponent.abs - 1) * int_part / n >= 0x7FFFFFFFFFFFFFFF
return ((xn.exponent > 0) ^ neg ? BigDecimal::INFINITY : BigDecimal(0)) * (int_part.even? || x > 0 ? 1 : -1)
return ((xn.exponent > 0) ^ neg ? BigMath._infinity_computation_result : BigDecimal(0)) * (int_part.even? || x > 0 ? 1 : -1)
end
end
return neg ? BigDecimal(1) / ans : ans
Expand All @@ -112,7 +119,7 @@ def power(y, prec = nil)
if y < 0
inv = x.power(-y, prec)
return BigDecimal(0) if inv.infinite?
return BigDecimal::INFINITY if inv.zero?
return BigMath._infinity_computation_result if inv.zero?
return BigDecimal(1).div(inv, prec)
end

Expand All @@ -138,7 +145,7 @@ def power(y, prec = nil)
# Core BigMath methods for BigDecimal (log, exp) are defined here.
# Other methods (sin, cos, atan) are defined in 'bigdecimal/math.rb'.
module BigMath
def self._coerce_to_bigdecimal(x, method_name, complex_domain_error = false)
def self._coerce_to_bigdecimal(x, method_name, complex_domain_error = false) # :nodoc:
case x
when BigDecimal
return x
Expand All @@ -152,11 +159,25 @@ def self._coerce_to_bigdecimal(x, method_name, complex_domain_error = false)
raise ArgumentError, "#{x.inspect} can't be coerced into BigDecimal"
end

def self._validate_prec(prec, method_name)
def self._validate_prec(prec, method_name) # :nodoc:
raise ArgumentError, 'precision must be an Integer' unless Integer === prec
raise ArgumentError, "Zero or negative precision for #{method_name}" if prec <= 0
end

def self._infinity_computation_result # :nodoc:
if BigDecimal.mode(BigDecimal::EXCEPTION_ALL).anybits?(BigDecimal::EXCEPTION_INFINITY)
raise FloatDomainError, "Computation results in 'Infinity'"
end
BigDecimal::INFINITY
end

def self._nan_computation_result # :nodoc:
if BigDecimal.mode(BigDecimal::EXCEPTION_ALL).anybits?(BigDecimal::EXCEPTION_NaN)
raise FloatDomainError, "Computation results to 'NaN'"
end
BigDecimal::NAN
end

# call-seq:
# BigMath.log(decimal, numeric) -> BigDecimal
#
Expand All @@ -172,9 +193,9 @@ def self._validate_prec(prec, method_name)
def self.log(x, prec)
_validate_prec(prec, :log)
x = _coerce_to_bigdecimal(x, :log, true)
return BigDecimal::NAN if x.nan?
return _nan_computation_result if x.nan?
raise Math::DomainError, 'Zero or negative argument for log' if x <= 0
return BigDecimal::INFINITY if x.infinite?
return _infinity_computation_result if x.infinite?
return BigDecimal(0) if x == 1

if x > 10 || x < 0.1
Expand Down Expand Up @@ -238,8 +259,8 @@ def self.log(x, prec)
def self.exp(x, prec)
_validate_prec(prec, :exp)
x = _coerce_to_bigdecimal(x, :exp)
return BigDecimal::NAN if x.nan?
return x.positive? ? BigDecimal::INFINITY : BigDecimal(0) if x.infinite?
return _nan_computation_result if x.nan?
return x.positive? ? _infinity_computation_result : BigDecimal(0) if x.infinite?
return BigDecimal(1) if x.zero?
return BigDecimal(1).div(exp(-x, prec), prec) if x < 0

Expand Down
Loading
Loading