Checking if a Float is equivalent to an integer value in Ruby
Say I have the following code:
x = 0.8
y = 1.0
What's the best way of checking that y
is equivalent to an Integer
? At the moment I'm doing:
y.to_int == y
which works, but I feel like there should be a better way.开发者_JAVA技巧
You mod
the value with 1, and check if the value equals 0.
if y % 1 == 0
TL;DR
Generally, you should use ==
to compare numbers when you don't care about the numeric type, which should be most of the time. When you really do care about the type, you should use the object-equality comparison inherited from Numeric#eql?.
The Simplest Thing That Could Possibly Work
You can just ask a numeric object if it's an integer. For example, Numeric#integer? lets you ask a number to check itself and return a Boolean value:
[1, 1.2, 1.02e+1].map(&:integer?)
#=> [true, false, false]
If all you care about is finding out whether y is an integer, then this is the way to go. Using your own examples:
y = 1
y.integer?
#=> true
y = 1.0
y.integer?
#=> false
Other Solutions
If you're trying to do something more complicated, like trying to avoid automatic Numeric type conversions in equality comparisons, the only real limit is your imagination and the idea you're trying to express clearly in your code. There are many methods in Numeric, Float, Integer, Object, String, and other classes that would allow you to perform type-conversations and strict equality comparisons. A few examples follow.
Use #eql?
Use various methods to convert to an Integer, and then check with strict object equality:
y = 1.2
# All of these will return false.
y.eql? y.truncate
y.eql? y.floor
y.eql? y.to_i
y.eql? Integer(y)
Check for Remainders with #zero?
If you want to create a Boolean expression without the automatic numeric conversions made by ==
, you can use the class-specific method inherited from Numeric#zero?. For example:
(y % 1).zero?
y.modulo(1).zero?
Use #ceil or #floor with Subtraction
If modulo doesn't do the trick for you with certain types of numbers, then you can use Float#ceil or Float#floor:
y = 1.2
(y - y.floor).zero?
#=> false
y = 1.02e+1
(y.floor - y).zero?
#=> false
You probably don't even want to do that. Floating point arithmetic is subject to rounding errors, and a series of operations that you would think gives e.g. 6.0 may actually give 5.9999999999999 . In that case, any check that the value is integer will fail, even though you probably intended it to succeed.
Normally it is a better approach to compare the float to an integer version within a given precision, like if (x - x.to_i).abs < 0.001
.
精彩评论