Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unsafe number ops #7226

Merged
merged 7 commits into from
Jan 3, 2019
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
23 changes: 23 additions & 0 deletions spec/compiler/codegen/convert_spec.cr
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions spec/std/big/big_decimal_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions spec/std/big/big_float_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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") }
Expand Down
8 changes: 4 additions & 4 deletions spec/std/big/big_int_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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)
straight-shoota marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
18 changes: 18 additions & 0 deletions spec/std/big/big_rational_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 36 additions & 0 deletions spec/std/int_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
42 changes: 42 additions & 0 deletions spec/std/number_spec.cr
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
101 changes: 90 additions & 11 deletions src/big/big_decimal.cr
Original file line number Diff line number Diff line change
Expand Up @@ -307,57 +307,91 @@ 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.
bcardiff marked this conversation as resolved.
Show resolved Hide resolved
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.
def to_i
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,
Expand All @@ -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
Expand All @@ -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)
Expand Down
Loading