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
53 changes: 51 additions & 2 deletions spec/std/big/big_decimal_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -377,8 +377,23 @@ describe BigDecimal do
(BigDecimal.new("7.5") > 6.6).should be_true
(6.6 < BigDecimal.new("7.5")).should be_true

BigDecimal.new("6.6").should eq(6.6)
6.6.should eq(BigDecimal.new("6.6"))
"1.0000000000000002".to_big_d.should be < 1.0.next_float
(1.0.to_big_d + 0.5.to_big_d ** 52).should eq(1.0.next_float)
"1.0000000000000003".to_big_d.should be > 1.0.next_float

1.0.next_float.should be > "1.0000000000000002".to_big_d
1.0.next_float.should eq(1.0.to_big_d + 0.5.to_big_d ** 52)
1.0.next_float.should be < "1.0000000000000003".to_big_d

0.to_big_d.should be < Float64::INFINITY
(Float64::MAX.to_big_d ** 7).should be < Float64::INFINITY
0.to_big_d.should be > -Float64::INFINITY
(Float64::MIN.to_big_d ** 7).should be > -Float64::INFINITY

Float64::INFINITY.should be > 0.to_big_d
Float64::INFINITY.should be > (Float64::MAX.to_big_d ** 7)
(-Float64::INFINITY).should be < 0.to_big_d
(-Float64::INFINITY).should be < (Float64::MIN.to_big_d ** 7)

(BigDecimal.new("6.5") > 7).should be_false
(BigDecimal.new("7.5") > 6).should be_true
Expand All @@ -388,6 +403,40 @@ describe BigDecimal do

BigRational.new(1, 2).should eq(BigDecimal.new("0.5"))
BigRational.new(1, 4).should eq(BigDecimal.new("0.25"))

(1.to_big_d / 3).should be < BigRational.new(1, 3)
(-(1.to_big_d / 3)).should be > BigRational.new(-1, 3)
(-1.to_big_d / 3).should be < BigRational.new(-1, 3)

BigRational.new(1, 3).should be > 1.to_big_d / 3
BigRational.new(-1, 3).should be < -(1.to_big_d / 3)
BigRational.new(-1, 3).should be > -1.to_big_d / 3

(1.to_big_d / 3 + BigDecimal.new(1, BigDecimal::DEFAULT_PRECISION)).should be > BigRational.new(1, 3)
(-(1.to_big_d / 3) - BigDecimal.new(1, BigDecimal::DEFAULT_PRECISION)).should be < BigRational.new(-1, 3)

BigRational.new(1, 3).should be < (1.to_big_d / 3 + BigDecimal.new(1, BigDecimal::DEFAULT_PRECISION))
BigRational.new(-1, 3).should be > (-(1.to_big_d / 3) - BigDecimal.new(1, BigDecimal::DEFAULT_PRECISION))

(0.5.to_big_d ** 10000).should eq(0.5.to_big_f ** 10000)
"5.0123727492064520093e-3011".to_big_d.should be > 0.5.to_big_f ** 10000

(0.5.to_big_f ** 10000).should eq(0.5.to_big_d ** 10000)
(0.5.to_big_f ** 10000).should be < "5.0123727492064520093e-3011".to_big_d
end

describe "#<=>" do
it "compares against NaNs" do
(1.to_big_d <=> Float64::NAN).should be_nil
(1.to_big_d <=> Float32::NAN).should be_nil
(Float64::NAN <=> 1.to_big_d).should be_nil
(Float32::NAN <=> 1.to_big_d).should be_nil

typeof(1.to_big_d <=> Float64::NAN).should eq(Int32?)
typeof(1.to_big_d <=> Float32::NAN).should eq(Int32?)
typeof(Float64::NAN <=> 1.to_big_d).should eq(Int32?)
typeof(Float32::NAN <=> 1.to_big_d).should eq(Int32?)
end
end

it "keeps precision" do
Expand Down
41 changes: 36 additions & 5 deletions src/big/big_decimal.cr
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,9 @@ struct BigDecimal < Number
# Divides `self` with another `BigDecimal`, with an optionally configurable
# *precision*.
#
# When the division is inexact, the returned value's scale is never greater
# than `scale - other.scale + precision`.
# When the division is inexact, the returned value rounds towards negative
# infinity, and its scale is never greater than
# `scale - other.scale + precision`.
#
# ```
# BigDecimal.new(1).div(BigDecimal.new(2)) # => BigDecimal(@value=5, @scale=2)
Expand Down Expand Up @@ -325,7 +326,30 @@ struct BigDecimal < Number
end
end

def <=>(other : Int | Float | BigRational)
def <=>(other : BigRational) : Int32
if @scale == 0
@value <=> other
else
# `@value / power_ten_to(@scale) <=> other.numerator / other.denominator`
@value * other.denominator <=> power_ten_to(@scale) * other.numerator
end
end

def <=>(other : Float::Primitive) : Int32?
return nil if other.nan?

if sign = other.infinite?
return -sign
end

self <=> other.to_big_r
end

def <=>(other : BigFloat) : Int32
self <=> other.to_big_r
end

def <=>(other : Int)
self <=> BigDecimal.new(other)
end

Expand Down Expand Up @@ -784,7 +808,8 @@ struct Float
include Comparable(BigDecimal)

def <=>(other : BigDecimal)
to_big_d <=> other
cmp = other <=> self
-cmp if cmp
end

# Converts `self` to `BigDecimal`.
Expand All @@ -800,11 +825,17 @@ struct Float
end
end

struct BigFloat
def <=>(other : BigDecimal)
-(other <=> self)
end
end

struct BigRational
include Comparable(BigDecimal)

def <=>(other : BigDecimal)
to_big_d <=> other
-(other <=> self)
end

# Converts `self` to `BigDecimal`.
Expand Down
5 changes: 4 additions & 1 deletion src/big/big_int.cr
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,11 @@ struct BigInt < Int
def **(other : Int) : BigInt
if other < 0
raise ArgumentError.new("Negative exponent isn't supported")
elsif other == 1
self
else
BigInt.new { |mpz| LibGMP.pow_ui(mpz, self, other) }
end
BigInt.new { |mpz| LibGMP.pow_ui(mpz, self, other) }
end

# Returns the greatest common divisor of `self` and *other*.
Expand Down
9 changes: 7 additions & 2 deletions src/big/big_rational.cr
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,16 @@ struct BigRational < Number
end

def numerator : BigInt
BigInt.new { |mpz| LibGMP.mpq_get_num(mpz, self) }
# Returns `LibGMP.mpq_numref(self)`, whose C macro expansion effectively
# produces a raw member access. This is only as safe as copying `BigInt`s by
# value, as both involve copying `LibGMP::MPZ` around which has reference
# semantics, and `BigInt`s cannot be safely mutated in-place this way; see
# #9825 for details. Ditto for `#denominator`.
BigInt.new(@mpq._mp_num)
end

def denominator : BigInt
BigInt.new { |mpz| LibGMP.mpq_get_den(mpz, self) }
BigInt.new(@mpq._mp_den)
end

def <=>(other : BigRational)
Expand Down