diff --git a/lib/bigdecimal/math.rb b/lib/bigdecimal/math.rb index 16e095df..a98ca84f 100644 --- a/lib/bigdecimal/math.rb +++ b/lib/bigdecimal/math.rb @@ -24,6 +24,8 @@ # log10(x, prec) # log1p(x, prec) # expm1(x, prec) +# frexp(x) +# ldexp(x, exponent) # PI (prec) # E (prec) == exp(1.0,prec) # @@ -568,6 +570,35 @@ def expm1(x, prec) exp_prec > 0 ? BigMath.exp(x, exp_prec).sub(1, prec) : BigDecimal(-1) end + # call-seq: + # frexp(x) -> [BigDecimal, Integer] + # + # Decomposes +x+ into a normalized fraction and an integral power of ten. + # + # BigMath.frexp(BigDecimal(123.456)) + # #=> [0.123456e0, 3] + # + def frexp(x) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, 0, :frexp) + return [x, 0] unless x.finite? + + exponent = x.exponent + [x._decimal_shift(-exponent), exponent] + end + + # call-seq: + # ldexp(fraction, exponent) -> BigDecimal + # + # Inverse of +frexp+. + # Returns the value of fraction * 10**exponent. + # + # BigMath.ldexp(BigDecimal("0.123456e0"), 3) + # #=> 0.123456e3 + # + def ldexp(x, exponent) + x = BigDecimal::Internal.coerce_to_bigdecimal(x, 0, :frexp) + x.finite? ? x._decimal_shift(exponent) : x + end # call-seq: # PI(numeric) -> BigDecimal diff --git a/test/bigdecimal/test_bigmath.rb b/test/bigdecimal/test_bigmath.rb index 1147a337..9f0edb60 100644 --- a/test/bigdecimal/test_bigmath.rb +++ b/test/bigdecimal/test_bigmath.rb @@ -469,4 +469,28 @@ def test_expm1 assert_in_exact_precision(BigMath.exp(BigDecimal("1.23e-10"), 120) - 1, expm1(BigDecimal("1.23e-10"), 100), 100) assert_in_exact_precision(BigMath.exp(123, 120) - 1, expm1(BigDecimal("123"), 100), 100) end + + def test_frexp + BigDecimal.save_limit do + BigDecimal.limit(3) + assert_equal([BigDecimal("-0.123456"), 10], BigMath.frexp(BigDecimal("-0.123456e10"))) + assert_equal([BigDecimal("0.123456"), -10], BigMath.frexp(BigDecimal("0.123456e-10"))) + assert_equal([BigDecimal("0.123456789"), 9], BigMath.frexp(123456789)) + assert_equal([BigDecimal(0), 0], BigMath.frexp(BigDecimal(0))) + assert_equal([BigDecimal::NAN, 0], BigMath.frexp(BigDecimal::NAN)) + assert_equal([BigDecimal::INFINITY, 0], BigMath.frexp(BigDecimal::INFINITY)) + end + end + + def test_ldexp + BigDecimal.save_limit do + BigDecimal.limit(3) + assert_equal(BigDecimal("-0.123456e10"), BigMath.ldexp(BigDecimal("-0.123456"), 10)) + assert_equal(BigDecimal("0.123456e20"), BigMath.ldexp(BigDecimal("0.123456e10"), 10.9)) + assert_equal(BigDecimal("0.123456e-10"), BigMath.ldexp(BigDecimal("0.123456"), -10)) + assert_equal(BigDecimal("0.123456789e19"), BigMath.ldexp(123456789, 10)) + assert(BigMath.ldexp(BigDecimal::NAN, 10).nan?) + assert_equal(BigDecimal::INFINITY, BigMath.ldexp(BigDecimal::INFINITY, 10)) + end + end end