class RuboCop::Cop::Lint::FloatComparison
# www.embeddeduse.com/2019/08/26/qt-compare-two-floats/
# Or some other epsilon based type of comparison:
Float(x, exception: false) == nil
# good - comparing against nil
(x - 0.1).abs < tolerance
tolerance = 0.0001
# good
(x - 0.1).abs < Float::EPSILON
# good
x != 0.0
x == 0.0
# good - comparing against zero
x.to_d == 0.1.to_d
# good - using BigDecimal
x != 0.1
x == 0.1
# bad
@example
if you perform any arithmetic operations involving precision loss.
floating-point value representation to be exactly the same, which is very unlikely
is almost never the desired semantics. Comparison via the ‘==/!=` operators checks
Floating point values are inherently inaccurate, and comparing them for exact equality
Checks for the presence of precise comparison of floating point numbers.
def check_numeric_returning_method(node)
def check_numeric_returning_method(node) return false unless node.receiver case node.method_name when :angle, :arg, :phase Float(node.receiver.source).negative? when :ceil, :floor, :round, :truncate precision = node.first_argument precision&.int_type? && Integer(precision.source).positive? end end
def check_send(node)
def check_send(node) if node.arithmetic_operation? float?(node.receiver) || float?(node.first_argument) elsif FLOAT_RETURNING_METHODS.include?(node.method_name) true elsif node.receiver&.float_type? if FLOAT_INSTANCE_METHODS.include?(node.method_name) true else check_numeric_returning_method(node) end end end
def float?(node)
def float?(node) return false unless node case node.type when :float true when :send check_send(node) when :begin float?(node.children.first) else false end end
def literal_safe?(node)
def literal_safe?(node) return false unless node (node.numeric_type? && node.value.zero?) || node.nil_type? end
def on_send(node)
def on_send(node) return unless node.arguments.one? lhs = node.receiver rhs = node.first_argument return if literal_safe?(lhs) || literal_safe?(rhs) add_offense(node) if float?(lhs) || float?(rhs) end