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 # bad case value when 1.0 foo when 2.0 bar end # good - using BigDecimal x.to_d == 0.1.to_d # good - comparing against zero x == 0.0 x != 0.0 # good (x - 0.1).abs < Float::EPSILON # good tolerance = 0.0001 (x - 0.1).abs < tolerance # good - comparing against nil Float(x, exception: false) == nil # good - using epsilon comparison in case expression case when (value - 1.0).abs < Float::EPSILON foo when (value - 2.0).abs < Float::EPSILON bar end # 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_CASE
- MSG_EQUALITY
- MSG_INEQUALITY
- RESTRICT_ON_SEND
Public Instance Methods
Source
# File lib/rubocop/cop/lint/float_comparison.rb, line 78 def on_case(node) node.when_branches.each do |when_branch| when_branch.each_condition do |condition| next if !float?(condition) || literal_safe?(condition) add_offense(condition, message: MSG_CASE) end end end
Source
# File lib/rubocop/cop/lint/float_comparison.rb, line 65 def on_send(node) return unless node.arguments.one? lhs = node.receiver rhs = node.first_argument return if literal_safe?(lhs) || literal_safe?(rhs) message = node.method?(:!=) ? MSG_INEQUALITY : MSG_EQUALITY add_offense(node, message: message) if float?(lhs) || float?(rhs) end
Also aliased as: on_csend
Private Instance Methods
Source
# File lib/rubocop/cop/lint/float_comparison.rb, line 90 def float?(node) return false unless node case node.type when :float true when :send float_send?(node) when :begin float?(node.children.first) else false end end
Source
# File lib/rubocop/cop/lint/float_comparison.rb, line 111 def float_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? FLOAT_INSTANCE_METHODS.include?(node.method_name) || numeric_returning_method?(node) end end
Source
# File lib/rubocop/cop/lint/float_comparison.rb, line 105 def literal_safe?(node) return false unless node (node.numeric_type? && node.value.zero?) || node.nil_type? end
Source
# File lib/rubocop/cop/lint/float_comparison.rb, line 122 def 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