3

How can I find out if a Float value is a negative zero (and not a positive one)?

Unfortunately:

-0.0 == 0.0   # => true
-0.0 === 0.0  # => true

My initial solution works but is ugly:

x.to_s == '-0.0' 

From this question, I found

x == 0 and 1 / x < 0

Is there a better, more Ruby-like way?

Community
  • 1
  • 1
undur_gongor
  • 15,657
  • 5
  • 63
  • 75

5 Answers5

7

Ruby's BigDecimal class has a sign method that produces the correct result for negative zero. You can convert a Float to a BigDecimal with the to_d method if you require 'bigdecimal/util'.

require 'bigdecimal'
require 'bigdecimal/util'

0.0.to_d.sign
#=> 1

-0.0.to_d.sign
#=> -1

Combine this with zero? and you're good to go:

def negative_zero?(x)
  x.zero? && x.to_d.sign == -1
end

negative_zero?(0.0)
#=> false

negative_zero?(-0.0)
#=> true
Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168
4

The angle method (and it's aliases arg and phase) returns zero for positive floats and Pi for negatives.

p 0.0.angle  #=> 0
p -0.0.angle #=> 3.141592653589793
steenslag
  • 79,051
  • 16
  • 138
  • 171
2

In Ruby the Float equality operator for -0.0 and 0.0 returns true, as per ordinary arithmetic.

However if you convert the two floats to bytes using little-endian or big-endian byte order, you'll see they do not in fact match.

[-0.0].pack('E')
#=> "\x00\x00\x00\x00\x00\x00\x00\x80"

[0.0].pack('E')
#=> "\x00\x00\x00\x00\x00\x00\x00\x00"

[-0.0].pack('E') == [0.0].pack('E')
#=> false
undur_gongor
  • 15,657
  • 5
  • 63
  • 75
Rots
  • 5,506
  • 3
  • 43
  • 51
2

If your purpose is to prevent "negative zero", then this is how rails does it:

number = number.abs if number.zero?
Tom Lord
  • 27,404
  • 4
  • 50
  • 77
0

Cause ruby determines them as the same object the only way to detect it by "-" sign after string conversion, as you described: -0.0.to_s.start_with?('-').

Tensho
  • 763
  • 1
  • 7
  • 23