Skip to content

Commit

Permalink
Disallow creating Big* numbers from infinity or NaN (#13351)
Browse files Browse the repository at this point in the history
  • Loading branch information
HertzDevil committed May 8, 2023
1 parent 54d6469 commit c9de730
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 13 deletions.
10 changes: 10 additions & 0 deletions spec/std/big/big_decimal_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,16 @@ describe BigDecimal do
end
end

it "raises if creating from infinity" do
expect_raises(ArgumentError, "Can only construct from a finite number") { BigDecimal.new(Float32::INFINITY) }
expect_raises(ArgumentError, "Can only construct from a finite number") { BigDecimal.new(Float64::INFINITY) }
end

it "raises if creating from NaN" do
expect_raises(ArgumentError, "Can only construct from a finite number") { BigDecimal.new(Float32::NAN) }
expect_raises(ArgumentError, "Can only construct from a finite number") { BigDecimal.new(Float64::NAN) }
end

it "performs arithmetic with bigdecimals" do
BigDecimal.new(0).should eq(BigDecimal.new(0) + BigDecimal.new(0))
BigDecimal.new(1).should eq(BigDecimal.new(0) + BigDecimal.new(1))
Expand Down
14 changes: 14 additions & 0 deletions spec/std/big/big_float_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,20 @@ describe "BigFloat" do
BigFloat.new(-2147483648_i32).to_s.should eq("-2147483648.0")
BigFloat.new(-9223372036854775808_i64).to_s.should eq("-9.223372036854775808e+18")
end

it "raises if creating from infinity" do
expect_raises(ArgumentError, "Can only construct from a finite number") { BigFloat.new(Float32::INFINITY) }
expect_raises(ArgumentError, "Can only construct from a finite number") { BigFloat.new(Float64::INFINITY) }
expect_raises(ArgumentError, "Can only construct from a finite number") { BigFloat.new(Float32::INFINITY, precision: 128) }
expect_raises(ArgumentError, "Can only construct from a finite number") { BigFloat.new(Float64::INFINITY, precision: 128) }
end

it "raises if creating from NaN" do
expect_raises(ArgumentError, "Can only construct from a finite number") { BigFloat.new(Float32::NAN) }
expect_raises(ArgumentError, "Can only construct from a finite number") { BigFloat.new(Float64::NAN) }
expect_raises(ArgumentError, "Can only construct from a finite number") { BigFloat.new(Float32::NAN, precision: 128) }
expect_raises(ArgumentError, "Can only construct from a finite number") { BigFloat.new(Float64::NAN, precision: 128) }
end
end

describe "#<=>" do
Expand Down
10 changes: 10 additions & 0 deletions spec/std/big/big_int_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ describe "BigInt" do
end
end

it "raises if creating from infinity" do
expect_raises(ArgumentError, "Can only construct from a finite number") { BigInt.new(Float32::INFINITY) }
expect_raises(ArgumentError, "Can only construct from a finite number") { BigInt.new(Float64::INFINITY) }
end

it "raises if creating from NaN" do
expect_raises(ArgumentError, "Can only construct from a finite number") { BigInt.new(Float32::NAN) }
expect_raises(ArgumentError, "Can only construct from a finite number") { BigInt.new(Float64::NAN) }
end

it "creates from float" do
BigInt.new(12.3).to_s.should eq("12")
end
Expand Down
38 changes: 25 additions & 13 deletions spec/std/big/big_rational_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,36 @@ private def test_comp(val, less, equal, greater, file = __FILE__, line = __LINE_
end

describe BigRational do
it "initialize" do
BigRational.new(BigInt.new(10), BigInt.new(3))
.should eq(BigRational.new(10, 3))
describe ".new" do
it "initialize" do
BigRational.new(BigInt.new(10), BigInt.new(3))
.should eq(BigRational.new(10, 3))

expect_raises(DivisionByZeroError) do
BigRational.new(BigInt.new(2), BigInt.new(0))
end

expect_raises(DivisionByZeroError) do
BigRational.new(2, 0)
end
end

expect_raises(DivisionByZeroError) do
BigRational.new(BigInt.new(2), BigInt.new(0))
it "initializes from BigFloat with high precision" do
(0..12).each do |i|
bf = BigFloat.new(2.0, precision: 64) ** 64 + BigFloat.new(2.0, precision: 64) ** i
br = BigRational.new(bf)
br.should eq(bf)
end
end

expect_raises(DivisionByZeroError) do
BigRational.new(2, 0)
it "raises if creating from infinity" do
expect_raises(ArgumentError, "Can only construct from a finite number") { BigRational.new(Float32::INFINITY) }
expect_raises(ArgumentError, "Can only construct from a finite number") { BigRational.new(Float64::INFINITY) }
end
end

it "initializes from BigFloat with high precision" do
(0..12).each do |i|
bf = BigFloat.new(2.0, precision: 64) ** 64 + BigFloat.new(2.0, precision: 64) ** i
br = BigRational.new(bf)
br.should eq(bf)
it "raises if creating from NaN" do
expect_raises(ArgumentError, "Can only construct from a finite number") { BigRational.new(Float32::NAN) }
expect_raises(ArgumentError, "Can only construct from a finite number") { BigRational.new(Float64::NAN) }
end
end

Expand Down
1 change: 1 addition & 0 deletions src/big/big_decimal.cr
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct BigDecimal < Number
# NOTE: Floats are fundamentally less precise than BigDecimals,
# which makes initialization from them risky.
def self.new(num : Float)
raise ArgumentError.new "Can only construct from a finite number" unless num.finite?
new(num.to_s)
end

Expand Down
6 changes: 6 additions & 0 deletions src/big/big_float.cr
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,17 @@ struct BigFloat < Float
end
end

def initialize(num : Float::Primitive)
raise ArgumentError.new "Can only construct from a finite number" unless num.finite?
LibGMP.mpf_init_set_d(out @mpf, num)
end

def initialize(num : Number)
LibGMP.mpf_init_set_d(out @mpf, num.to_f64)
end

def initialize(num : Float, precision : Int)
raise ArgumentError.new "Can only construct from a finite number" unless num.finite?
LibGMP.mpf_init2(out @mpf, precision.to_u64)
LibGMP.mpf_set_d(self, num.to_f64)
end
Expand Down
3 changes: 3 additions & 0 deletions src/big/big_int.cr
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@ struct BigInt < Int
end

# :ditto:
#
# *num* must be finite.
def initialize(num : Float::Primitive)
raise ArgumentError.new "Can only construct from a finite number" unless num.finite?
LibGMP.init_set_d(out @mpz, num)
end

Expand Down
1 change: 1 addition & 0 deletions src/big/big_rational.cr
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ struct BigRational < Number

# Creates a exact representation of float as rational.
def initialize(num : Float::Primitive)
raise ArgumentError.new "Can only construct from a finite number" unless num.finite?
# It ensures that `BigRational.new(f) == f`
# It relies on fact, that mantissa is at most 53 bits
frac, exp = Math.frexp num
Expand Down

0 comments on commit c9de730

Please sign in to comment.