Exact equality comparison against a floating-point literal
ID |
vbnet.correctness.float_equality |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
low |
Resource |
Numeric |
Language |
VB.NET |
Description
Flags an exact equality or inequality test in which one operand is a floating-point literal —
a value with a decimal point such as 21.5, an exponent form such as 1.0E-6 or 1E-9, or a
literal carrying an F, R, ! or # suffix — on either side of the operator. Because =
also means assignment in VB.NET, the equality form is matched only where the context makes it a
comparison: inside an If … Then condition or in a Return. The <> form is matched
anywhere. Two things are deliberately outside its reach: a comparison in which both sides are
variables, since no literal is present to key on, and Decimal literals written with the D
or @ suffix, which do compare exactly and are the correct type for money. Ordering
comparisons such as reading > 21.5 are well behaved and are not reported.
Rationale
Double and Single are binary floating point and cannot represent most decimal fractions
exactly, so a value that is conceptually equal to the literal generally differs from it in the
last bits once any arithmetic, parsing or unit conversion has touched it. The test then fails
where the author expects it to succeed — and with <>, succeeds where they expect it to fail.
The failure mode is the awkward kind: the comparison is not reliably wrong, so the branch fires
for some inputs and not others, and the outcome can shift between compilers, platforms and
optimisation settings. A thermostat that never registers reaching its set point, or a loop that
never sees its terminating value, are typical results.
The following code illustrates the pattern detected by this rule:
Public Function AtTarget(ByVal reading As Double) As Boolean
' FLAGGED: Exact equality comparison against a floating-point literal
If reading = 21.5 Then
Return True
End If
Return False
End Function
Remediation
Compare against a tolerance instead of testing for exact equality. When the values must compare
exactly, as with money, use Decimal rather than Double or Single.
' Before: rarely True once `reading` has been through any arithmetic
If reading = 21.5 Then
HoldTemperature()
End If
' After
If Math.Abs(reading - 21.5) < 0.001 Then
HoldTemperature()
End If