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
2 changes: 1 addition & 1 deletion spec/manual/string_to_f32_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe "x.to_s.to_f32 == x" do
val.nan?.should be_true
else
val.should eq(float)
Math.copysign(1, val).should eq(Math.copysign(1, float))
val.sign_bit.should eq(float.sign_bit)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions spec/std/complex_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ require "../support/number"
# exact equality, including component signs
private def assert_complex_eq(z1 : Complex, z2 : Complex, *, file = __FILE__, line = __LINE__)
z1.should eq(z2), file: file, line: line
Math.copysign(1.0, z1.real).should eq(Math.copysign(1.0, z2.real)), file: file, line: line
Math.copysign(1.0, z1.imag).should eq(Math.copysign(1.0, z2.imag)), file: file, line: line
z1.real.sign_bit.should eq(z2.real.sign_bit), file: file, line: line
z1.imag.sign_bit.should eq(z2.imag.sign_bit), file: file, line: line
end

private def assert_complex_nan(z : Complex, *, file = __FILE__, line = __LINE__)
Expand Down
28 changes: 24 additions & 4 deletions spec/std/float_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -442,14 +442,14 @@ describe "Float" do
end

it "#abs" do
Math.copysign(1_f64, 0.0_f64.abs).should eq 1_f64
Math.copysign(1_f64, -0.0_f64.abs).should eq 1_f64
0.0_f64.abs.sign_bit.should eq 1
-0.0_f64.abs.sign_bit.should eq 1

0.1_f64.abs.should eq 0.1_f64
-0.1_f64.abs.should eq 0.1_f64

Math.copysign(1_f32, 0.0_f32.abs).should eq 1_f32
Math.copysign(1_f32, -0.0_f32.abs).should eq 1_f32
0.0_f32.abs.sign_bit.should eq 1_f32
-0.0_f32.abs.sign_bit.should eq 1_f32

0.1_f32.abs.should eq 0.1_f32
-0.1_f32.abs.should eq 0.1_f32
Expand All @@ -464,4 +464,24 @@ describe "Float" do
Float32::INFINITY.abs.should eq Float32::INFINITY
(-Float32::INFINITY).abs.should eq Float32::INFINITY
end

it "#sign_bit" do
1.2_f64.sign_bit.should eq(1)
-1.2_f64.sign_bit.should eq(-1)
0.0_f64.sign_bit.should eq(1)
-0.0_f64.sign_bit.should eq(-1)
Float64::INFINITY.sign_bit.should eq(1)
(-Float64::INFINITY).sign_bit.should eq(-1)
0x7ff0_0000_0000_0001_u64.unsafe_as(Float64).sign_bit.should eq(1)
0xfff0_0000_0000_0001_u64.unsafe_as(Float64).sign_bit.should eq(-1)

1.2_f32.sign_bit.should eq(1)
-1.2_f32.sign_bit.should eq(-1)
0.0_f32.sign_bit.should eq(1)
-0.0_f32.sign_bit.should eq(-1)
Float32::INFINITY.sign_bit.should eq(1)
(-Float32::INFINITY).sign_bit.should eq(-1)
0x7f80_0001_u32.unsafe_as(Float32).sign_bit.should eq(1)
0xff80_0001_u32.unsafe_as(Float32).sign_bit.should eq(-1)
end
end
2 changes: 1 addition & 1 deletion src/complex.cr
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ struct Complex
# ```
def to_s(io : IO) : Nil
io << @real
io << (@imag.nan? || Math.copysign(1.0, @imag) > 0 ? " + " : " - ")
io << (@imag.nan? || @imag.sign_bit > 0 ? " + " : " - ")
io << Math.copysign(@imag, 1.0)
io << 'i'
end
Expand Down
14 changes: 14 additions & 0 deletions src/float.cr
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,13 @@ struct Float32
Math.copysign(self, 1)
end

# Returns `-1` if the sign bit of this float is set, `1` otherwise.
#
# Unlike `#sign`, this works on signed zeros and not-a-numbers as well.
def sign_bit : Int32
Math.copysign(1_f32, self).to_i
end

# Rounds towards positive infinity.
def ceil : Float32
LibM.ceil_f32(self)
Expand Down Expand Up @@ -390,6 +397,13 @@ struct Float64
Math.copysign(self, 1)
end

# Returns `-1` if the sign bit of this float is set, `1` otherwise.
#
# Unlike `#sign`, this works on signed zeros and not-a-numbers as well.
def sign_bit : Int32
Math.copysign(1_f64, self).to_i
end

def ceil : Float64
LibM.ceil_f64(self)
end
Expand Down
2 changes: 1 addition & 1 deletion src/humanize.cr
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ struct Number
integer, _, decimals = string.partition('.')
end

is_negative = number.is_a?(Float::Primitive) ? Math.copysign(1, number) < 0 : number < 0
is_negative = number.responds_to?(:sign_bit) ? number.sign_bit < 0 : number < 0

format_impl(io, is_negative, integer, decimals, separator, delimiter, decimal_places, group, only_significant)
end
Expand Down
8 changes: 4 additions & 4 deletions src/string/formatter.cr
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ struct String::Formatter(A)
printf_size = Float::Printer::RyuPrintf.d2fixed_buffered_n(float, printf_precision, printf_buf.to_unsafe)
printf_slice = printf_buf.to_slice[0, printf_size]
dot_index = printf_slice.index('.'.ord)
sign = Math.copysign(1.0, float)
sign = float.sign_bit

str_size = printf_size + trailing_zeros
str_size += 1 if sign < 0 || flags.plus || flags.space
Expand Down Expand Up @@ -431,7 +431,7 @@ struct String::Formatter(A)
printf_slice = printf_buf.to_slice[0, printf_size]
dot_index = printf_slice.index('.'.ord)
e_index = printf_slice.rindex!('e'.ord)
sign = Math.copysign(1.0, float)
sign = float.sign_bit

printf_slice[e_index] = 'E'.ord.to_u8! if flags.uppercase?

Expand Down Expand Up @@ -469,7 +469,7 @@ struct String::Formatter(A)
printf_slice = printf_buf.to_slice[0, printf_size]
dot_index = printf_slice.index('.'.ord)
e_index = printf_slice.rindex('e'.ord)
sign = Math.copysign(1.0, float)
sign = float.sign_bit

printf_slice[e_index] = 'E'.ord.to_u8! if e_index && flags.uppercase?

Expand Down Expand Up @@ -499,7 +499,7 @@ struct String::Formatter(A)

# Formats floats with `%a` or `%A`
private def float_hex(float, flags)
sign = Math.copysign(1.0, float)
sign = float.sign_bit
float = float.abs

str_size = Float::Printer::Hexfloat(Float64, UInt64).to_s_size(float,
Expand Down