Skip to content

Commit

Permalink
Additions to Big arithmetics (#4653)
Browse files Browse the repository at this point in the history
* Additions to Big arithmetics.
Follows #4560.

* Follow @RX14 review (single line blocks, extract mantissa bits constants).

* Int cast is implicit here.

* Format src/big/big_rational.cr

* BigRational is not float, and frexp should be not used on it.

* frexp should not be used on BigInt.

* rework BigFloat./

* rename bsi, bfsi, bsf, bfsf etc. in spec.

* rework def BigFloat.<=>(other : Int)

* Simplify a bit, thanks to @RX14

* just remove commen. looks like single Number constructor looks OK in every case.

* more verbose naming for used variables.

* just explanation of workaround, refs #4897

* For now overloads work (i don't know why).

* dedup

* Forward declarations of Big Arithmentics types.
  • Loading branch information
akzhan authored and RX14 committed Sep 1, 2017
1 parent 842548e commit d1960a5
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 20 deletions.
72 changes: 72 additions & 0 deletions spec/std/big/big_float_spec.cr
Expand Up @@ -2,6 +2,62 @@ require "spec"
require "big_float"

describe "BigFloat" do
describe "new" do
string_of_integer_value = "123456789012345678901"
bigfloat_of_integer_value = BigFloat.new(string_of_integer_value)
string_of_float_value = "1234567890.12345678901"
bigfloat_of_float_value = BigFloat.new(string_of_float_value)

it "new(String)" do
bigfloat_of_integer_value.to_s.should eq(string_of_integer_value)
bigfloat_of_float_value.to_s.should eq(string_of_float_value)
end

it "new(BigInt)" do
bigfloat_on_bigint_value = BigFloat.new(BigInt.new(string_of_integer_value))
bigfloat_on_bigint_value.should eq(bigfloat_of_integer_value)
bigfloat_on_bigint_value.to_s.should eq(string_of_integer_value)
end

it "new(BigRational)" do
bigfloat_on_bigrational_value = BigFloat.new(BigRational.new(1, 3))
bigfloat_on_bigrational_value.should eq(BigFloat.new(1) / BigFloat.new(3))
end

it "new(BigFloat)" do
BigFloat.new(bigfloat_of_integer_value).should eq(bigfloat_of_integer_value)
BigFloat.new(bigfloat_of_float_value).should eq(bigfloat_of_float_value)
end

it "new(Int)" do
BigFloat.new(1_u8).to_s.should eq("1")
BigFloat.new(1_u16).to_s.should eq("1")
BigFloat.new(1_u32).to_s.should eq("1")
BigFloat.new(1_u64).to_s.should eq("1")
BigFloat.new(1_i8).to_s.should eq("1")
BigFloat.new(1_i16).to_s.should eq("1")
BigFloat.new(1_i32).to_s.should eq("1")
BigFloat.new(1_i64).to_s.should eq("1")
BigFloat.new(-1_i8).to_s.should eq("-1")
BigFloat.new(-1_i16).to_s.should eq("-1")
BigFloat.new(-1_i32).to_s.should eq("-1")
BigFloat.new(-1_i64).to_s.should eq("-1")

BigFloat.new(255_u8).to_s.should eq("255")
BigFloat.new(65535_u16).to_s.should eq("65535")
BigFloat.new(4294967295_u32).to_s.should eq("4294967295")
BigFloat.new(18446744073709551615_u64).to_s.should eq("18446744073709551615")
BigFloat.new(127_i8).to_s.should eq("127")
BigFloat.new(32767_i16).to_s.should eq("32767")
BigFloat.new(2147483647_i32).to_s.should eq("2147483647")
BigFloat.new(9223372036854775807_i64).to_s.should eq("9223372036854775807")
BigFloat.new(-128_i8).to_s.should eq("-128")
BigFloat.new(-32768_i16).to_s.should eq("-32768")
BigFloat.new(-2147483648_i32).to_s.should eq("-2147483648")
BigFloat.new(-9223372036854775808_i64).to_s.should eq("-9223372036854775808")
end
end

describe "-@" do
bf = "0.12345".to_big_f
it { (-bf).to_s.should eq("-0.12345") }
Expand Down Expand Up @@ -40,6 +96,8 @@ describe "BigFloat" do
it { ("-5.5".to_big_f / "5.5".to_big_f).to_s.should eq("-1") }
it { ("5.5".to_big_f / "-5.5".to_big_f).to_s.should eq("-1") }
expect_raises(DivisionByZero) { 0.1.to_big_f / 0 }
it { ("5.5".to_big_f / 16_u64).to_s.should eq("0.34375") }
it { ("5.5".to_big_f / 16_u8).to_s.should eq("0.34375") }
end

describe "**" do
Expand All @@ -65,6 +123,13 @@ describe "BigFloat" do
describe "floor" do
it { 2.1.to_big_f.floor.should eq(2) }
it { 2.9.to_big_f.floor.should eq(2) }
it { -2.9.to_big_f.floor.should eq(-3) }
end

describe "trunc" do
it { 2.1.to_big_f.trunc.should eq(2) }
it { 2.9.to_big_f.trunc.should eq(2) }
it { -2.9.to_big_f.trunc.should eq(-2) }
end

describe "to_f" do
Expand Down Expand Up @@ -92,6 +157,7 @@ describe "BigFloat" do
it { "12345678.87654321".to_big_f.to_s.should eq("12345678.87654321") }
it { "9.000000000000987".to_big_f.to_s.should eq("9.000000000000987") }
it { "12345678901234567".to_big_f.to_s.should eq("12345678901234567") }
it { "1234567890123456789".to_big_f.to_s.should eq("1234567890123456789") }
end

describe "#inspect" do
Expand All @@ -108,3 +174,9 @@ describe "BigFloat" do
x.clone.should eq(x)
end
end

describe "BigFloat Math" do
it "frexp" do
Math.frexp(0.2.to_big_f).should eq({0.8, -2})
end
end
28 changes: 28 additions & 0 deletions spec/std/big/big_int_spec.cr
Expand Up @@ -53,6 +53,29 @@ describe "BigInt" do
[1.1, 1.to_big_i, 3.to_big_i, 2.2].sort.should eq([1, 1.1, 2.2, 3])
end

it "divides and calculs the modulo" do
11.to_big_i.divmod(3.to_big_i).should eq({3, 2})
11.to_big_i.divmod(-3.to_big_i).should eq({-4, -1})

11.to_big_i.divmod(3_i32).should eq({3, 2})
11.to_big_i.divmod(-3_i32).should eq({-4, -1})

10.to_big_i.divmod(2).should eq({5, 0})
11.to_big_i.divmod(2).should eq({5, 1})

10.to_big_i.divmod(2.to_big_i).should eq({5, 0})
11.to_big_i.divmod(2.to_big_i).should eq({5, 1})

10.to_big_i.divmod(-2).should eq({-5, 0})
11.to_big_i.divmod(-2).should eq({-6, -1})

-10.to_big_i.divmod(2).should eq({-5, 0})
-11.to_big_i.divmod(2).should eq({-6, 1})

-10.to_big_i.divmod(-2).should eq({5, 0})
-11.to_big_i.divmod(-2).should eq({5, -1})
end

it "adds" do
(1.to_big_i + 2.to_big_i).should eq(3.to_big_i)
(1.to_big_i + 2).should eq(3.to_big_i)
Expand Down Expand Up @@ -222,6 +245,11 @@ describe "BigInt" do
a.to_s(32).should eq(d)
end

it "does to_big_f" do
a = BigInt.new("1234567890123456789")
a.to_big_f.should eq(BigFloat.new("1234567890123456789.0"))
end

describe "#inspect" do
it { "2".to_big_i.inspect.should eq("2_big_i") }
end
Expand Down
14 changes: 14 additions & 0 deletions spec/std/big/big_rational_spec.cr
Expand Up @@ -72,10 +72,24 @@ describe BigRational do
r.to_f32.should be_close(f, 0.001)
end

it "#to_big_f" do
r = br(10, 3)
f = 10.to_big_f / 3.to_big_f
r.to_big_f.should be_close(f, 0.001)
end

it "Int#to_big_r" do
3.to_big_r.should eq(br(3, 1))
end

it "Float32#to_big_r" do
0.3333333333333333333333_f32.to_big_r.should eq(br(11184811, 33554432))
end

it "Float64#to_big_r" do
0.3333333333333333333333_f64.to_big_r.should eq(br(6004799503160661, 18014398509481984))
end

it "#<=>(:BigRational) and Comparable" do
a = br(11, 3)
l = br(10, 3)
Expand Down
10 changes: 10 additions & 0 deletions src/big.cr
@@ -1,3 +1,13 @@
# :nodoc: Forward declarations
struct BigInt < Int
end

struct BigFloat < Float
end

struct BigRational < Number
end

require "./big/lib_gmp"
require "./big/big_int"
require "./big/big_float"
Expand Down
83 changes: 76 additions & 7 deletions src/big/big_float.cr
Expand Up @@ -17,6 +17,47 @@ struct BigFloat < Float
LibGMP.mpf_init_set_str(out @mpf, str, 10)
end

def initialize(num : BigInt)
LibGMP.mpf_init(out @mpf)
LibGMP.mpf_set_z(self, num)
end

def initialize(num : BigRational)
LibGMP.mpf_init(out @mpf)
LibGMP.mpf_set_q(self, num)
end

def initialize(num : BigFloat)
LibGMP.mpf_init(out @mpf)
LibGMP.mpf_set(self, num)
end

def initialize(num : Int8 | Int16 | Int32)
LibGMP.mpf_init_set_si(out @mpf, num)
end

def initialize(num : UInt8 | UInt16 | UInt32)
LibGMP.mpf_init_set_ui(out @mpf, num)
end

def initialize(num : Int64)
if LibGMP::Long == Int64
LibGMP.mpf_init_set_si(out @mpf, num)
else
LibGMP.mpf_init(out @mpf)
LibGMP.mpf_set_z(self, num.to_big_i)
end
end

def initialize(num : UInt64)
if LibGMP::ULong == UInt64
LibGMP.mpf_init_set_ui(out @mpf, num)
else
LibGMP.mpf_init(out @mpf)
LibGMP.mpf_set_z(self, num.to_big_i)
end
end

def initialize(num : Number)
LibGMP.mpf_init_set_d(out @mpf, num.to_f64)
end
Expand Down Expand Up @@ -51,16 +92,22 @@ struct BigFloat < Float
LibGMP.mpf_cmp(self, other)
end

def <=>(other : Float)
LibGMP.mpf_cmp_d(self, other.to_f64)
def <=>(other : BigInt)
LibGMP.mpf_cmp_z(self, other)
end

def <=>(other : Int::Signed)
LibGMP.mpf_cmp_si(self, other.to_i64)
def <=>(other : Float32 | Float64)
LibGMP.mpf_cmp_d(self, other.to_f64)
end

def <=>(other : Int::Unsigned)
LibGMP.mpf_cmp_ui(self, other.to_u64)
def <=>(other : Number)
if other.is_a?(Int8 | Int16 | Int32) || (LibGMP::Long == Int64 && other.is_a?(Int64))
LibGMP.mpf_cmp_si(self, other)
elsif other.is_a?(UInt8 | UInt16 | UInt32) || (LibGMP::ULong == UInt64 && other.is_a?(UInt64))
LibGMP.mpf_cmp_ui(self, other)
else
LibGMP.mpf_cmp(self, other.to_big_f)
end
end

def -
Expand All @@ -81,7 +128,11 @@ struct BigFloat < Float

def /(other : Number)
raise DivisionByZero.new if other == 0
BigFloat.new { |mpf| LibGMP.mpf_div(mpf, self, other.to_big_f) }
if other.is_a?(UInt8 | UInt16 | UInt32) || (LibGMP::ULong == UInt64 && other.is_a?(UInt64))
BigFloat.new { |mpf| LibGMP.mpf_div_ui(mpf, self, other) }
else
BigFloat.new { |mpf| LibGMP.mpf_div(mpf, self, other.to_big_f) }
end
end

def **(other : Int)
Expand All @@ -100,6 +151,10 @@ struct BigFloat < Float
BigFloat.new { |mpf| LibGMP.mpf_floor(mpf, self) }
end

def trunc
BigFloat.new { |mpf| LibGMP.mpf_trunc(mpf, self) }
end

def to_f64
LibGMP.mpf_get_d(self)
end
Expand Down Expand Up @@ -222,3 +277,17 @@ class String
BigFloat.new(self)
end
end

module Math
def frexp(value : BigFloat)
LibGMP.mpf_get_d_2exp(out exp, value) # we need BigFloat frac, so will skip Float64 one.
frac = BigFloat.new do |mpf|
if exp >= 0
LibGMP.mpf_div_2exp(mpf, value, exp)
else
LibGMP.mpf_mul_2exp(mpf, value, -exp)
end
end
{frac, exp}
end
end

0 comments on commit d1960a5

Please sign in to comment.