tags:

views:

2308

answers:

6

In ruby, I want to convert a float to an int if it's a whole number. For example

a = 1.0
b = 2.5

a.to_int_if_whole # => 1
b.to_int_if_whole # => 2.5

Basically I'm trying to avoid displaying ".0" on any number that doesn't have a decimal. I'm looking for an elegant (or built-in) way to do

def to_int_if_whole(float)
  (float % 1 == 0) ? float.to_i : float
end
+2  A: 

I'm don't know much about Ruby.

But this is a display issue. I would be extremely surprised if the libraries you are using don't have a way to format a number when you convert it to a string.

There might not be a catch-all formatting option that does exactly what you want but you could set up a method that returns true if the float is the float representation of a whole number and false otherwise. Inside a formatting routine that you create (so you only have to do this in once place) just change the formatting based on if this is true or false.

This discusses how to control the number of digits that appear after the decimal when displaying a number.

Watch out for the intricacies of floating point representations. Math might say the answer is 3 but you may get 3.000000000000000000001. I'd suggest using a delta to see if the number is almost an integer number.

colithium
This is true, but I want it to display this way *everywhere* a float outputs as a string. Basically I want to override the Float.to_s method, or hang this logic on the end of it.
Eric Wright
+1  A: 

Although I'd tend to agree with the above post, if you must do this:

(float == float.floor) ? float.to_i : float
A: 

I don't know much about Ruby either.

But in C++, I'd do this:

bool IsWholeNumber( float f )
{
    const float delta = 0.0001;
    int i = (int) f;
    return (f - (float)i) < delta;
}

And then I'd format the output precision based on that.

Geerad
+5  A: 

One simple way to it would be:

class Float
  def prettify
    to_i == self ? to_i : self
  end
end

That's because:

irb> 1.0 == 1
=> true
irb> 1 == 1.0
=> true

Then you could do:

irb> 1.0.prettify
=> 1

irb> 1.5.prettify
=> 1.5
Yehuda Katz
+1  A: 

Here's my horribly hacktastic implementation provided for educational purposes:

class Float
  def to_int_if_whole(precision = 2)
    ("%.#{precision}f" % self).split(/\./).last == '0' * precision and self.to_i or self
  end
end

puts 1.0.to_int_if_whole # => 1
puts 2.5.to_int_if_whole # => 2.5
puts 1.9999999999999999999923.to_int_if_whole # => 2

The reason for using the sprintf-style call is that it handles floating point approximations much more reliably than the Float#round method tends to.

tadman
+1  A: 

This is the solution that ended up working the way I want it to:

class Float
  alias_method(:original_to_s, :to_s) unless method_defined?(:original_to_s)

  def is_whole?
    self % 1 == 0
  end

  def to_s
    self.is_whole? ? self.to_i.to_s : self.original_to_s
  end
end

This way I can update the is_whole? logic (I seems like tadman's is the most sophisticated) if needed, and it ensures that anywhere a Float outputs to a string (eg, in a form) it appears the way I want it to (ie, no zeros on the end).

Thanks to everybody for your ideas - they really helped.

Eric Wright