Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement floating-point manipulation functions for BigFloat #11007

Merged
46 changes: 46 additions & 0 deletions spec/std/big/big_float_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -456,8 +456,54 @@ describe "BigFloat" do
end

describe "BigFloat Math" do
it ".ilogb" do
Math.ilogb(0.2.to_big_f).should eq(-3)
Math.ilogb(123.45.to_big_f).should eq(6)
Math.ilogb(2.to_big_f ** 1_000_000_000_000).should eq(1_000_000_000_000)
end

it ".logb" do
Math.ilogb(0.2.to_big_f).should eq(-3.to_big_f)
Math.ilogb(123.45.to_big_f).should eq(6.to_big_f)
Math.ilogb(2.to_big_f ** 1_000_000_000_000).should eq(1_000_000_000_000.to_big_f)
end

it ".ldexp" do
Math.ldexp(0.2.to_big_f, 2).should eq(0.8.to_big_f)
Math.ldexp(0.2.to_big_f, -2).should eq(0.05.to_big_f)
Math.ldexp(1.to_big_f, 1_000_000_000_000).should eq(2.to_big_f ** 1_000_000_000_000)
Math.ldexp(1.to_big_f, -1_000_000_000_000).should eq(0.5.to_big_f ** 1_000_000_000_000)
end

it ".scalbn" do
Math.scalbn(0.2.to_big_f, 2).should eq(0.8.to_big_f)
Math.scalbn(0.2.to_big_f, -2).should eq(0.05.to_big_f)
Math.scalbn(1.to_big_f, 1_000_000_000_000).should eq(2.to_big_f ** 1_000_000_000_000)
Math.scalbn(1.to_big_f, -1_000_000_000_000).should eq(0.5.to_big_f ** 1_000_000_000_000)
end

it ".scalbln" do
Math.scalbln(0.2.to_big_f, 2).should eq(0.8.to_big_f)
Math.scalbln(0.2.to_big_f, -2).should eq(0.05.to_big_f)
Math.scalbln(1.to_big_f, 1_000_000_000_000).should eq(2.to_big_f ** 1_000_000_000_000)
Math.scalbln(1.to_big_f, -1_000_000_000_000).should eq(0.5.to_big_f ** 1_000_000_000_000)
end

it ".frexp" do
Math.frexp(0.2.to_big_f).should eq({0.8, -2})
Math.frexp(2.to_big_f ** 1_000_000_000_000).should eq({0.5, 1_000_000_000_001})
end

it ".copysign" do
Math.copysign(3.to_big_f, 2.to_big_f).should eq(3.to_big_f)
Math.copysign(3.to_big_f, 0.to_big_f).should eq(3.to_big_f)
Math.copysign(3.to_big_f, -2.to_big_f).should eq(-3.to_big_f)
Math.copysign(0.to_big_f, 2.to_big_f).should eq(0.to_big_f)
Math.copysign(0.to_big_f, 0.to_big_f).should eq(0.to_big_f)
Math.copysign(0.to_big_f, -2.to_big_f).should eq(0.to_big_f)
Math.copysign(-3.to_big_f, 2.to_big_f).should eq(3.to_big_f)
Math.copysign(-3.to_big_f, 0.to_big_f).should eq(3.to_big_f)
Math.copysign(-3.to_big_f, -2.to_big_f).should eq(-3.to_big_f)
end

it ".sqrt" do
Expand Down
48 changes: 48 additions & 0 deletions src/big/big_float.cr
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,43 @@ class String
end

module Math
# Returns the unbiased base 2 exponent of the given floating-point *value*.
def ilogb(value : BigFloat) : Int64
LibGMP.mpf_get_d_2exp(out exp, value)
(exp - 1).to_i64
end

# Returns the unbiased radix-independent exponent of the given floating-point *value*.
#
# For `BigFloat` this is equivalent to `ilogb`.
def logb(value : BigFloat) : BigFloat
LibGMP.mpf_get_d_2exp(out exp, value)
(exp - 1).to_big_f
HertzDevil marked this conversation as resolved.
Show resolved Hide resolved
end

# Multiplies the given floating-point *value* by 2 raised to the power *exp*.
def ldexp(value : BigFloat, exp : Int) : BigFloat
BigFloat.new do |mpf|
if exp >= 0
LibGMP.mpf_mul_2exp(mpf, value, exp.to_u64)
else
LibGMP.mpf_div_2exp(mpf, value, exp.abs.to_u64)
end
end
end

# Returns the floating-point *value* with its exponent raised by *exp*.
#
# For `BigFloat` this is equivalent to `ldexp`.
def scalbn(value : BigFloat, exp : Int) : BigFloat
ldexp(value, exp)
end

# :ditto:
def scalbln(value : BigFloat, exp : Int) : BigFloat
ldexp(value, exp)
end

# Decomposes the given floating-point *value* into a normalized fraction and an integral power of two.
def frexp(value : BigFloat) : {BigFloat, Int32 | Int64}
LibGMP.mpf_get_d_2exp(out exp, value) # we need BigFloat frac, so will skip Float64 one.
Expand All @@ -406,6 +443,17 @@ module Math
{frac, exp}
end

# Returns the floating-point value with the magnitude of *value1* and the sign of *value2*.
#
# `BigFloat` does not support signed zeros; if `value2 == 0`, the returned value is non-negative.
def copysign(value1 : BigFloat, value2 : BigFloat) : BigFloat
if value1.negative? != value2.negative? # opposite signs
-value1
else
value1
end
end

# Calculates the square root of *value*.
#
# ```
Expand Down