class RuboCop::Cop::Lint::FloatComparison

Checks for the presence of precise comparison of floating point numbers.

Floating point values are inherently inaccurate, and comparing them for exact equality is almost never the desired semantics. Comparison via the ‘==/!=` operators checks floating-point value representation to be exactly the same, which is very unlikely if you perform any arithmetic operations involving precision loss.

@example

# bad
x == 0.1
x != 0.1

# good - using BigDecimal
x.to_d == 0.1.to_d

# good
(x - 0.1).abs < Float::EPSILON

# good
tolerance = 0.0001
(x - 0.1).abs < tolerance

# Or some other epsilon based type of comparison:
# https://www.embeddeduse.com/2019/08/26/qt-compare-two-floats/

Constants

EQUALITY_METHODS
FLOAT_INSTANCE_METHODS
FLOAT_RETURNING_METHODS
MSG
RESTRICT_ON_SEND

Public Instance Methods

on_send(node) click to toggle source
# File lib/rubocop/cop/lint/float_comparison.rb, line 40
def on_send(node)
  lhs, _method, rhs = *node
  add_offense(node) if float?(lhs) || float?(rhs)
end

Private Instance Methods

check_numeric_returning_method(node) click to toggle source

rubocop:enable Metrics/PerceivedComplexity

# File lib/rubocop/cop/lint/float_comparison.rb, line 79
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
check_send(node) click to toggle source

rubocop:disable Metrics/PerceivedComplexity

# File lib/rubocop/cop/lint/float_comparison.rb, line 63
def check_send(node)
  if node.arithmetic_operation?
    lhs, _operation, rhs = *node
    float?(lhs) || float?(rhs)
  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
float?(node) click to toggle source
# File lib/rubocop/cop/lint/float_comparison.rb, line 47
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