diff --git a/spec/compiler/codegen/convert_spec.cr b/spec/compiler/codegen/convert_spec.cr new file mode 100644 index 000000000000..69afd94b4927 --- /dev/null +++ b/spec/compiler/codegen/convert_spec.cr @@ -0,0 +1,23 @@ +require "../../spec_helper" + +describe "Code gen: convert primitives" do + describe "to_*!" do + it "works from negative values to unsigned types" do + run(%( + -1.to_u! == 4294967295_u32 + )).to_b.should be_true + end + + it "works from greater values to smaller types" do + run(%( + 47866.to_i8! == -6_i8 + )).to_b.should be_true + end + + it "preserves negative sign" do + run(%( + -1_i8.to_i! == -1_i32 + )).to_b.should be_true + end + end +end diff --git a/spec/std/big/big_decimal_spec.cr b/spec/std/big/big_decimal_spec.cr index ff6cf71b3117..25f215a90171 100644 --- a/spec/std/big/big_decimal_spec.cr +++ b/spec/std/big/big_decimal_spec.cr @@ -323,6 +323,21 @@ describe BigDecimal do bd2.to_f.should eq -0.00123 bd3.to_f.should eq 123.0 bd4.to_f.should eq -123.0 + + bd1.to_i!.should eq 0 + bd2.to_i!.should eq 0 + bd3.to_i!.should eq 123 + bd4.to_i!.should eq -123 + + bd1.to_u!.should eq 0 + bd2.to_u!.should eq 0 + bd3.to_u!.should eq 123 + bd4.to_u!.should eq 123 + + bd1.to_f!.should eq 0.00123 + bd2.to_f!.should eq -0.00123 + bd3.to_f!.should eq 123.0 + bd4.to_f!.should eq -123.0 end it "hashes" do diff --git a/spec/std/big/big_float_spec.cr b/spec/std/big/big_float_spec.cr index 7a7f00a54682..18bcc04ae8e1 100644 --- a/spec/std/big/big_float_spec.cr +++ b/spec/std/big/big_float_spec.cr @@ -147,18 +147,36 @@ describe "BigFloat" do it { 1.234567.to_big_f.to_f32.should eq(1.234567_f32) } end + describe "to_f!" do + it { 1.34.to_big_f.to_f!.should eq(1.34) } + it { 0.0001304.to_big_f.to_f!.should eq(0.0001304) } + it { 1.234567.to_big_f.to_f32!.should eq(1.234567_f32) } + end + describe "to_i" do it { 1.34.to_big_f.to_i.should eq(1) } it { 123.to_big_f.to_i.should eq(123) } it { -4321.to_big_f.to_i.should eq(-4321) } end + describe "to_i!" do + it { 1.34.to_big_f.to_i!.should eq(1) } + it { 123.to_big_f.to_i!.should eq(123) } + it { -4321.to_big_f.to_i!.should eq(-4321) } + end + describe "to_u" do it { 1.34.to_big_f.to_u.should eq(1) } it { 123.to_big_f.to_u.should eq(123) } it { 4321.to_big_f.to_u.should eq(4321) } end + describe "to_u!" do + it { 1.34.to_big_f.to_u!.should eq(1) } + it { 123.to_big_f.to_u!.should eq(123) } + it { 4321.to_big_f.to_u!.should eq(4321) } + end + describe "to_s" do it { "0".to_big_f.to_s.should eq("0") } it { "0.000001".to_big_f.to_s.should eq("0.000001") } diff --git a/spec/std/big/big_int_spec.cr b/spec/std/big/big_int_spec.cr index 6fb89c919d25..b254eda9f381 100644 --- a/spec/std/big/big_int_spec.cr +++ b/spec/std/big/big_int_spec.cr @@ -296,13 +296,13 @@ describe "BigInt" do it "can be casted into other Number types" do big = BigInt.new(1234567890) big.to_i.should eq(1234567890) - big.to_i8.should eq(-46) - big.to_i16.should eq(722) + big.to_i8!.should eq(-46) + big.to_i16!.should eq(722) big.to_i32.should eq(1234567890) big.to_i64.should eq(1234567890) big.to_u.should eq(1234567890) - big.to_u8.should eq(210) - big.to_u16.should eq(722) + big.to_u8!.should eq(210) + big.to_u16!.should eq(722) big.to_u32.should eq(1234567890) u64 = big.to_u64 diff --git a/spec/std/big/big_rational_spec.cr b/spec/std/big/big_rational_spec.cr index 702904be1255..a70d192dc334 100644 --- a/spec/std/big/big_rational_spec.cr +++ b/spec/std/big/big_rational_spec.cr @@ -60,18 +60,36 @@ describe BigRational do r.to_f64.should be_close(f, 0.001) end + it "#to_f64!" do + r = br(10, 3) + f = 10.to_f64 / 3.to_f64 + r.to_f64!.should be_close(f, 0.001) + end + it "#to_f" do r = br(10, 3) f = 10.to_f64 / 3.to_f64 r.to_f.should be_close(f, 0.001) end + it "#to_f!" do + r = br(10, 3) + f = 10.to_f64 / 3.to_f64 + r.to_f!.should be_close(f, 0.001) + end + it "#to_f32" do r = br(10, 3) f = 10.to_f32 / 3.to_f32 r.to_f32.should be_close(f, 0.001) end + it "#to_f32!" do + r = br(10, 3) + f = 10.to_f32 / 3.to_f32 + 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 diff --git a/spec/std/int_spec.cr b/spec/std/int_spec.cr index f96fe6aed3de..22bce4ee1297 100644 --- a/spec/std/int_spec.cr +++ b/spec/std/int_spec.cr @@ -46,6 +46,42 @@ describe "Int" do end end + describe "&**" do + it "with positive Int32" do + x = 2 &** 2 + x.should eq(4) + x.should be_a(Int32) + + x = 2 &** 0 + x.should eq(1) + x.should be_a(Int32) + end + + it "with UInt8" do + x = 2_u8 &** 2 + x.should eq(4) + x.should be_a(UInt8) + end + + it "raises with negative exponent" do + expect_raises(ArgumentError, "Cannot raise an integer to a negative integer power, use floats for that") do + 2 &** -1 + end + end + + it "works with large integers" do + x = 51_i64 &** 11 + x.should eq(6071163615208263051_i64) + x.should be_a(Int64) + end + + it "wraps with larger integers" do + x = 51_i64 &** 12 + x.should eq(-3965304877440961871_i64) + x.should be_a(Int64) + end + end + describe "#===(:Char)" do it { (99 === 'c').should be_true } it { (99_u8 === 'c').should be_true } diff --git a/spec/std/number_spec.cr b/spec/std/number_spec.cr index 6cdeac80e086..d11701c8c26d 100644 --- a/spec/std/number_spec.cr +++ b/spec/std/number_spec.cr @@ -1,6 +1,48 @@ require "spec" +private macro it_initializes_from_value_to(number_type) + it "initialize from value to {{number_type}}" do + {{number_type}}.new(1).should be_a({{number_type}}) + {{number_type}}.new(1).should eq(1) + + {{number_type}}.new(1u32).should be_a({{number_type}}) + {{number_type}}.new(1u32).should eq(1) + + {{number_type}}.new(1.0).should be_a({{number_type}}) + {{number_type}}.new(1.0).should eq(1) + end + + it "unchecked initialize from value to {{number_type}}" do + {{number_type}}.new!(1).should be_a({{number_type}}) + {{number_type}}.new!(1).should eq(1) + + {{number_type}}.new!(1u32).should be_a({{number_type}}) + {{number_type}}.new!(1u32).should eq(1) + + {{number_type}}.new!(1.0).should be_a({{number_type}}) + {{number_type}}.new!(1.0).should eq(1) + end +end + describe "Number" do + it_initializes_from_value_to Int8 + it_initializes_from_value_to Int16 + it_initializes_from_value_to Int32 + it_initializes_from_value_to Int64 + + it_initializes_from_value_to UInt8 + it_initializes_from_value_to UInt16 + it_initializes_from_value_to UInt32 + it_initializes_from_value_to UInt64 + + {% if flag?(:bits64) %} + it_initializes_from_value_to Int128 + it_initializes_from_value_to UInt128 + {% end %} + + it_initializes_from_value_to Float32 + it_initializes_from_value_to Float64 + describe "significant" do it "10 base" do 1234.567.significant(1).should eq(1000) diff --git a/src/big/big_decimal.cr b/src/big/big_decimal.cr index 8171e9122908..5efe0e9aefad 100644 --- a/src/big/big_decimal.cr +++ b/src/big/big_decimal.cr @@ -307,28 +307,33 @@ struct BigDecimal < Number self end - # Converts to `Int64`. Truncates anything on the right side of the decimal point. - def to_i64 + # Converts to `BigInt`. Truncates anything on the right side of the decimal point. + def to_big_i if @value >= 0 - (@value / TEN ** @scale).to_i64 + (@value / TEN ** @scale) else - -(@value.abs / TEN ** @scale).to_i64 + -(@value.abs / TEN ** @scale) end end + # Converts to `Int64`. Truncates anything on the right side of the decimal point. + def to_i64 + to_big_i.to_i64 + end + # Converts to `Int32`. Truncates anything on the right side of the decimal point. def to_i32 - to_i64.to_i32 + to_big_i.to_i32 end # Converts to `Int16`. Truncates anything on the right side of the decimal point. def to_i16 - to_i64.to_i16 + to_big_i.to_i16 end # Converts to `Int8`. Truncates anything on the right side of the decimal point. def to_i8 - to_i64.to_i8 + to_big_i.to_i8 end # Converts to `Int32`. Truncates anything on the right side of the decimal point. @@ -336,28 +341,57 @@ struct BigDecimal < Number to_i32 end + # Converts to `Int8`. Truncates anything on the right side of the decimal point. + def to_i8! + to_big_i.to_i8! + end + + # Converts to `Int16`. Truncates anything on the right side of the decimal point. + def to_i16! + to_big_i.to_i16! + end + + # Converts to `Int32`. Truncates anything on the right side of the decimal point. + def to_i32! + to_big_i.to_i32! + end + + # Converts to `Int64`. Truncates anything on the right side of the decimal point. + def to_i64! + to_big_i.to_i64! + end + + # Converts to `Int32`. Truncates anything on the right side of the decimal point. + def to_i! + to_i32! + end + + private def to_big_u + (@value.abs / TEN ** @scale) + end + # Converts to `UInt64`. Truncates anything on the right side of the decimal point, # converting negative to positive. def to_u64 - (@value.abs / TEN ** @scale).to_u64 + to_big_u.to_u64 end # Converts to `UInt32`. Truncates anything on the right side of the decimal point, # converting negative to positive. def to_u32 - to_u64.to_u32 + to_big_u.to_u32 end # Converts to `UInt16`. Truncates anything on the right side of the decimal point, # converting negative to positive. def to_u16 - to_u64.to_u16 + to_big_u.to_u16 end # Converts to `UInt8`. Truncates anything on the right side of the decimal point, # converting negative to positive. def to_u8 - to_u64.to_u8 + to_big_u.to_u8 end # Converts to `UInt32`. Truncates anything on the right side of the decimal point, @@ -366,6 +400,36 @@ struct BigDecimal < Number to_u32 end + # Converts to `UInt8`. Truncates anything on the right side of the decimal point, + # converting negative to positive. + def to_u8! + to_big_u.to_u8! + end + + # Converts to `UInt16`. Truncates anything on the right side of the decimal point, + # converting negative to positive. + def to_u16! + to_big_u.to_u16! + end + + # Converts to `UInt32`. Truncates anything on the right side of the decimal point, + # converting negative to positive. + def to_u32! + to_big_u.to_u32! + end + + # Converts to `UInt64`. Truncates anything on the right side of the decimal point, + # converting negative to positive. + def to_u64! + to_big_u.to_u64! + end + + # Converts to `UInt32`. Truncates anything on the right side of the decimal point, + # converting negative to positive. + def to_u! + to_u32! + end + # Converts to `Float64`. def to_f64 to_s.to_f64 @@ -381,6 +445,21 @@ struct BigDecimal < Number to_f64 end + # Converts to `Float32`. + def to_f32! + to_f64.to_f32! + end + + # Converts to `Float64`. + def to_f64! + to_f64 + end + + # Converts to `Float64`. + def to_f! + to_f64! + end + # Converts to `BigFloat`. def to_big_f BigFloat.new(to_s) diff --git a/src/big/big_float.cr b/src/big/big_float.cr index e582f99bbdac..e3132132f54a 100644 --- a/src/big/big_float.cr +++ b/src/big/big_float.cr @@ -172,6 +172,18 @@ struct BigFloat < Float to_f64 end + def to_f32! + to_f64.to_f32! + end + + def to_f64! + to_f64 + end + + def to_f! + to_f64! + end + def to_big_f self end @@ -196,6 +208,26 @@ struct BigFloat < Float to_i32 end + def to_i! + to_i32! + end + + def to_i8! + LibGMP.mpf_get_si(self).to_i8! + end + + def to_i16! + LibGMP.mpf_get_si(self).to_i16! + end + + def to_i32! + LibGMP.mpf_get_si(self).to_i32! + end + + def to_i64! + LibGMP.mpf_get_si(self) + end + def to_u64 LibGMP.mpf_get_ui(self) end @@ -216,6 +248,26 @@ struct BigFloat < Float to_u32 end + def to_u! + to_u32! + end + + def to_u8! + LibGMP.mpf_get_ui(self).to_u8! + end + + def to_u16! + LibGMP.mpf_get_ui(self).to_u16! + end + + def to_u32! + LibGMP.mpf_get_ui(self).to_u32! + end + + def to_u64! + LibGMP.mpf_get_ui(self) + end + def to_unsafe mpf end diff --git a/src/big/big_int.cr b/src/big/big_int.cr index 880ad36f254c..321841976207 100644 --- a/src/big/big_int.cr +++ b/src/big/big_int.cr @@ -407,13 +407,37 @@ struct BigInt < Int end def to_i64 - if LibGMP::Long == Int64 || (self <= Int32::MAX && self >= Int32::MIN) + if LibGMP::Long == Int64 || (Int32::MIN <= self <= Int32::MAX) LibGMP.get_si(self).to_i64 else to_s.to_i64 end end + def to_i! + to_i32! + end + + def to_i8! + LibGMP.get_si(self).to_i8! + end + + def to_i16! + LibGMP.get_si(self).to_i16! + end + + def to_i32! + LibGMP.get_si(self).to_i32! + end + + def to_i64! + if LibGMP::Long == Int64 || (Int32::MIN <= self <= Int32::MAX) + LibGMP.get_si(self).to_i64! + else + to_s.to_i64 + end + end + def to_u to_u32 end @@ -431,13 +455,37 @@ struct BigInt < Int end def to_u64 - if LibGMP::ULong == UInt64 || (self <= UInt32::MAX && self >= UInt32::MIN) + if LibGMP::ULong == UInt64 || (UInt32::MIN <= self <= UInt32::MAX) LibGMP.get_ui(self).to_u64 else to_s.to_u64 end end + def to_u! + to_u32! + end + + def to_u8! + LibGMP.get_ui(self).to_u8! + end + + def to_u16! + LibGMP.get_ui(self).to_u16! + end + + def to_u32! + LibGMP.get_ui(self).to_u32! + end + + def to_u64! + if LibGMP::Long == Int64 || (Int32::MIN <= self <= Int32::MAX) + LibGMP.get_ui(self).to_u64! + else + to_s.to_u64 + end + end + def to_f to_f64 end @@ -450,6 +498,18 @@ struct BigInt < Int LibGMP.get_d(self) end + def to_f! + to_f64! + end + + def to_f32! + LibGMP.get_d(self).to_f32! + end + + def to_f64! + LibGMP.get_d(self) + end + def to_big_i self end diff --git a/src/big/big_rational.cr b/src/big/big_rational.cr index f2ff5e961ecb..508b390f224a 100644 --- a/src/big/big_rational.cr +++ b/src/big/big_rational.cr @@ -177,6 +177,18 @@ struct BigRational < Number LibGMP.mpq_get_d(mpq) end + def to_f32! + to_f64.to_f32! + end + + def to_f64! + to_f64 + end + + def to_f! + to_f64! + end + def to_big_f BigFloat.new { |mpf| LibGMP.mpf_set_q(mpf, mpq) } end diff --git a/src/compiler/crystal/codegen/primitives.cr b/src/compiler/crystal/codegen/primitives.cr index b749f7c573f5..39025479c857 100644 --- a/src/compiler/crystal/codegen/primitives.cr +++ b/src/compiler/crystal/codegen/primitives.cr @@ -18,6 +18,11 @@ class Crystal::CodeGenVisitor when "binary" codegen_primitive_binary node, target_def, call_args when "cast" + # TODO 0.28.0 delete :cast + codegen_primitive_cast node, target_def, call_args + when "convert" + codegen_primitive_cast node, target_def, call_args + when "unchecked_convert" codegen_primitive_cast node, target_def, call_args when "allocate" codegen_primitive_allocate node, target_def, call_args diff --git a/src/enum.cr b/src/enum.cr index ee953b53f15d..8abd09a8196a 100644 --- a/src/enum.cr +++ b/src/enum.cr @@ -164,6 +164,11 @@ struct Enum def to_{{name.id}} : {{type}} value.to_{{name.id}} end + + # Returns the value of this enum member as a `{{type}}` + def to_{{name.id}}! : {{type}} + value.to_{{name.id}}! + end {% end %} # Returns the enum member that results from adding *other* diff --git a/src/float.cr b/src/float.cr index fed35eb35987..0ff554bc2b57 100644 --- a/src/float.cr +++ b/src/float.cr @@ -139,6 +139,11 @@ struct Float32 value.to_f32 end + # Returns a `Float32` by invoking `to_f32!` on *value*. + def self.new!(value) + value.to_f32! + end + def ceil LibM.ceil_f32(self) end @@ -223,6 +228,11 @@ struct Float64 value.to_f64 end + # Returns a `Float64` by invoking `to_f64!` on *value*. + def Float64.new!(value) + value.to_f64! + end + def ceil LibM.ceil_f64(self) end diff --git a/src/int.cr b/src/int.cr index 297c97ffccc4..f09ca4bdffd2 100644 --- a/src/int.cr +++ b/src/int.cr @@ -274,8 +274,33 @@ struct Int k = self while exponent > 0 result *= k if exponent & 0b1 != 0 - k *= k exponent = exponent.unsafe_shr(1) + k *= k if exponent > 0 + end + result + end + + # Returns the value of raising `self` to the power of *exponent*. + # + # Raises `ArgumentError` if *exponent* is negative: if this is needed, + # either use a float base or a float exponent. + # + # ``` + # 2 &** 3 # => 8 + # 2 &** 0 # => 1 + # 2 &** -1 # ArgumentError + # ``` + def &**(exponent : Int) : self + if exponent < 0 + raise ArgumentError.new "Cannot raise an integer to a negative integer power, use floats for that" + end + + result = self.class.new(1) + k = self + while exponent > 0 + result &*= k if exponent & 0b1 != 0 + exponent = exponent.unsafe_shr(1) + k &*= k if exponent > 0 end result end @@ -615,6 +640,11 @@ struct Int8 value.to_i8 end + # Returns an `Int8` by invoking `to_i8!` on *value*. + def self.new!(value) + value.to_i8! + end + def - 0_i8 - self end @@ -637,6 +667,11 @@ struct Int16 value.to_i16 end + # Returns an `Int16` by invoking `to_i16!` on *value*. + def self.new!(value) + value.to_i16! + end + def - 0_i16 - self end @@ -659,6 +694,11 @@ struct Int32 value.to_i32 end + # Returns an `Int32` by invoking `to_i32!` on *value*. + def self.new!(value) + value.to_i32! + end + def - 0 - self end @@ -681,6 +721,11 @@ struct Int64 value.to_i64 end + # Returns an `Int64` by invoking `to_i64!` on *value*. + def self.new!(value) + value.to_i64! + end + def - 0_i64 - self end @@ -704,6 +749,11 @@ struct Int128 value.to_i128 end + # Returns an `Int128` by invoking `to_i128!` on *value*. + def self.new!(value) + value.to_i128! + end + def - # TODO: use 0_i128 - self Int128.new(0) - self @@ -727,6 +777,11 @@ struct UInt8 value.to_u8 end + # Returns an `UInt8` by invoking `to_u8!` on *value*. + def self.new!(value) + value.to_u8! + end + def abs self end @@ -749,6 +804,11 @@ struct UInt16 value.to_u16 end + # Returns an `UInt16` by invoking `to_u16!` on *value*. + def self.new!(value) + value.to_u16! + end + def abs self end @@ -771,6 +831,11 @@ struct UInt32 value.to_u32 end + # Returns an `UInt32` by invoking `to_u32!` on *value*. + def self.new!(value) + value.to_u32! + end + def abs self end @@ -793,6 +858,11 @@ struct UInt64 value.to_u64 end + # Returns an `UInt64` by invoking `to_u64!` on *value*. + def self.new!(value) + value.to_u64! + end + def abs self end @@ -816,6 +886,11 @@ struct UInt128 value.to_u128 end + # Returns an `UInt128` by invoking `to_u128!` on *value*. + def self.new!(value) + value.to_u128! + end + def abs self end diff --git a/src/primitives.cr b/src/primitives.cr index f6adf07b3074..982e26412332 100644 --- a/src/primitives.cr +++ b/src/primitives.cr @@ -280,10 +280,19 @@ end to_u8: UInt8, to_u16: UInt16, to_u32: UInt32, to_u64: UInt64, to_u128: UInt128, to_f32: Float32, to_f64: Float64, } %} + # TODO 0.28.0 replace with @[Primitive(:convert)] + # Returns `self` converted to `{{type}}`. @[Primitive(:cast)] def {{name.id}} : {{type}} end + + # TODO 0.28.0 replace with @[Primitive(:unchecked_convert)] + + # Returns `self` converted to `{{type}}`. + @[Primitive(:cast)] + def {{name.id}}! : {{type}} + end {% end %} {% for num2 in nums %}