From 57d93972fcafdbfe260d3a06a7c363d96f2841c6 Mon Sep 17 00:00:00 2001 From: Quinton Miller <nicetas.c@gmail.com> Date: Fri, 3 Nov 2023 07:03:55 +0800 Subject: [PATCH] Add `Number#integer?` (#13936) --- spec/std/big/big_decimal_spec.cr | 12 ++++++++++++ spec/std/big/big_float_spec.cr | 6 ++++++ spec/std/big/big_int_spec.cr | 5 +++++ spec/std/big/big_rational_spec.cr | 9 +++++++++ spec/std/float_spec.cr | 17 +++++++++++++++++ spec/std/int_spec.cr | 7 +++++++ src/big/big_decimal.cr | 6 ++++++ src/big/big_float.cr | 3 ++- src/big/big_rational.cr | 7 +++++++ src/int.cr | 7 +++++++ src/number.cr | 16 ++++++++++++++++ 11 files changed, 94 insertions(+), 1 deletion(-) diff --git a/spec/std/big/big_decimal_spec.cr b/spec/std/big/big_decimal_spec.cr index dd307ee9812f..f6b0834ed2be 100644 --- a/spec/std/big/big_decimal_spec.cr +++ b/spec/std/big/big_decimal_spec.cr @@ -969,6 +969,18 @@ describe BigDecimal do (-1.to_big_d - BigDecimal.new(50000, 204)).round(200, mode: :ties_even).should eq(-1.to_big_d - BigDecimal.new(5, 200)) end end + + describe "#integer?" do + it { BigDecimal.new(0, 0).integer?.should be_true } + it { BigDecimal.new(1, 0).integer?.should be_true } + it { BigDecimal.new(10, 0).integer?.should be_true } + it { BigDecimal.new(-10, 1).integer?.should be_true } + it { BigDecimal.new(10000, 4).integer?.should be_true } + + it { BigDecimal.new(1, 1).integer?.should be_false } + it { BigDecimal.new(13, 2).integer?.should be_false } + it { BigDecimal.new(-2400, 3).integer?.should be_false } + end end describe "#inspect" do diff --git a/spec/std/big/big_float_spec.cr b/spec/std/big/big_float_spec.cr index 5c98beb320ca..679f271c5f3b 100644 --- a/spec/std/big/big_float_spec.cr +++ b/spec/std/big/big_float_spec.cr @@ -526,6 +526,12 @@ describe "BigFloat" do end end + describe "#integer?" do + it { BigFloat.zero.integer?.should be_true } + it { 1.to_big_f.integer?.should be_true } + it { 1.2.to_big_f.integer?.should be_false } + end + it "#hash" do b = 123.to_big_f b.hash.should eq(b.to_f64.hash) diff --git a/spec/std/big/big_int_spec.cr b/spec/std/big/big_int_spec.cr index f7eebc53ffb4..07575892bc08 100644 --- a/spec/std/big/big_int_spec.cr +++ b/spec/std/big/big_int_spec.cr @@ -9,6 +9,11 @@ private def it_converts_to_s(num, str, *, file = __FILE__, line = __LINE__, **op end describe "BigInt" do + describe "#integer?" do + it { BigInt.zero.integer?.should be_true } + it { 12345.to_big_i.integer?.should be_true } + end + it "creates with a value of zero" do BigInt.new.to_s.should eq("0") end diff --git a/spec/std/big/big_rational_spec.cr b/spec/std/big/big_rational_spec.cr index 2c9b138ba357..7b28a4938966 100644 --- a/spec/std/big/big_rational_spec.cr +++ b/spec/std/big/big_rational_spec.cr @@ -393,6 +393,15 @@ describe BigRational do end end + describe "#integer?" do + it { br(0, 1).integer?.should be_true } + it { br(1, 1).integer?.should be_true } + it { br(9, 3).integer?.should be_true } + it { br(-126, 7).integer?.should be_true } + it { br(5, 2).integer?.should be_false } + it { br(7, -3).integer?.should be_false } + end + it "#hash" do b = br(10, 3) hash = b.hash diff --git a/spec/std/float_spec.cr b/spec/std/float_spec.cr index 4ddfd194313d..9a6ebecd3a2f 100644 --- a/spec/std/float_spec.cr +++ b/spec/std/float_spec.cr @@ -76,6 +76,23 @@ describe "Float" do it { 2.9.ceil.should eq(3) } end + describe "#integer?" do + it { 1.0_f32.integer?.should be_true } + it { 1.0_f64.integer?.should be_true } + + it { 1.2_f32.integer?.should be_false } + it { 1.2_f64.integer?.should be_false } + + it { Float32::MAX.integer?.should be_true } + it { Float64::MAX.integer?.should be_true } + + it { Float32::INFINITY.integer?.should be_false } + it { Float64::INFINITY.integer?.should be_false } + + it { Float32::NAN.integer?.should be_false } + it { Float64::NAN.integer?.should be_false } + end + describe "fdiv" do it { 1.0.fdiv(1).should eq 1.0 } it { 1.0.fdiv(2).should eq 0.5 } diff --git a/spec/std/int_spec.cr b/spec/std/int_spec.cr index 4f377b385206..a4740521a7ec 100644 --- a/spec/std/int_spec.cr +++ b/spec/std/int_spec.cr @@ -13,6 +13,13 @@ private macro it_converts_to_s(num, str, **opts) end describe "Int" do + describe "#integer?" do + {% for int in BUILTIN_INTEGER_TYPES %} + it { {{ int }}::MIN.integer?.should be_true } + it { {{ int }}::MAX.integer?.should be_true } + {% end %} + end + describe "**" do it "with positive Int32" do x = 2 ** 2 diff --git a/src/big/big_decimal.cr b/src/big/big_decimal.cr index 376192c5124b..ab42c64bf919 100644 --- a/src/big/big_decimal.cr +++ b/src/big/big_decimal.cr @@ -459,6 +459,12 @@ struct BigDecimal < Number BigDecimal.new(mantissa, 0) end + # :inherit: + def integer? : Bool + factor_powers_of_ten + scale == 0 + end + def round(digits : Number, base = 10, *, mode : RoundingMode = :ties_even) : BigDecimal return self if zero? diff --git a/src/big/big_float.cr b/src/big/big_float.cr index 33231b422db3..5464d948931b 100644 --- a/src/big/big_float.cr +++ b/src/big/big_float.cr @@ -411,7 +411,8 @@ struct BigFloat < Float end end - protected def integer? + # :inherit: + def integer? : Bool !LibGMP.mpf_integer_p(mpf).zero? end diff --git a/src/big/big_rational.cr b/src/big/big_rational.cr index 6b6a6c0bb0b2..4b81db43f46b 100644 --- a/src/big/big_rational.cr +++ b/src/big/big_rational.cr @@ -179,6 +179,13 @@ struct BigRational < Number x end + # :inherit: + def integer? : Bool + # since all `BigRational`s are canonicalized, the denominator must be + # positive and coprime with the numerator + denominator == 1 + end + # Divides the rational by (2 ** *other*) # # ``` diff --git a/src/int.cr b/src/int.cr index 4f10b543fc67..3c78877c74be 100644 --- a/src/int.cr +++ b/src/int.cr @@ -274,6 +274,13 @@ struct Int self end + # :inherit: + # + # Always returns `true` for `Int`. + def integer? : Bool + true + end + # Returns the value of raising `self` to the power of *exponent*. # # Raises `ArgumentError` if *exponent* is negative: if this is needed, diff --git a/src/number.cr b/src/number.cr index 9aae19e1eb8a..9ec542f86ef7 100644 --- a/src/number.cr +++ b/src/number.cr @@ -343,6 +343,22 @@ struct Number end end + # Returns `true` if `self` is an integer. + # + # Non-integer types may return `true` as long as `self` denotes a finite value + # without any fractional parts. + # + # ``` + # 1.integer? # => true + # 1.0.integer? # => true + # 1.2.integer? # => false + # (1 / 0).integer? # => false + # (0 / 0).integer? # => false + # ``` + def integer? : Bool + self % 1 == 0 + end + # Returns `true` if `self` is equal to zero. # # ```