Skip to end of metadata
Go to start of metadata

The object representation for floating-point values is implementation defined. However, an implementation that defines the __STDC_IEC_559__ macro shall conform to the IEC 60559 floating-point standard and uses what is frequently referred to as IEEE 754 floating-point arithmetic [ISO/IEC 9899:2011]. The floating-point object representation used by IEC 60559 is one of the most common floating-point object representations in use today.

All floating-point object representations use specific bit patterns to encode the value of the floating-point number being represented. However, equivalence of floating-point values is not encoded solely by the bit pattern used to represent the value. For instance, if the floating-point format supports negative zero values (as IEC 60559 does), the values -0.0 and 0.0 are equivalent and will compare as equal, but the bit patterns used in the object representation are not identical. Similarly, if two floating-point values are both (the same) NaN, they will not compare as equal, despite the bit patterns being identical, because they are not equivalent.

Do not compare floating-point object representations directly, such as by calling memcmp()or its moral equivalents. Instead, the equality operators (== and !=) should be used to determine if two floating-point values are equivalent.

Noncompliant Code Example

In this noncompliant code example, memcmp() is used to compare two structures for equality. However, since the structure contains a floating-point object, this code may not behave as the programmer intended.

#include <stdbool.h>
#include <string.h>
 
struct S {
  int i;
  float f;
};
 
bool are_equal(const struct S *s1, const struct S *s2) {
  if (!s1 && !s2)
    return true;
  else if (!s1 || !s2)
    return false;
  return 0 == memcmp(s1, s2, sizeof(struct S));
}

Compliant Solution

In this compliant solution, the structure members are compared individually:

#include <stdbool.h>
#include <string.h>
 
struct S {
  int i;
  float f;
};
 
bool are_equal(const struct S *s1, const struct S *s2) {
  if (!s1 && !s2)
    return true;
  else if (!s1 || !s2)
    return false;
  return s1->i == s2->i &&
         s1->f == s2->f;
}

Risk Assessment

Using the object representation of a floating-point value for comparisons can lead to incorrect equality results, which can lead to unexpected behavior.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

FLP37-C

Low

Unlikely

Medium

P2

L3

Automated Detection

Tool

Version

Checker

Description

Astrée
20.10
memcmp-with-floatPartially checked
Axivion Bauhaus Suite

6.9.0

CertC-FLP37Fully implemented
LDRA tool suite
9.7.1
618 SEnhanced Enforcement
Parasoft C/C++test

10.4.2

CERT_C-FLP37-a
CERT_C-FLP37-b
CERT_C-FLP37-c

Avoid accessing arrays out of bounds
Pointer arithmetic should not be used
Do not use object representations to compare floating-point values

Polyspace Bug Finder

R2020a

CERT C: Rule FLP37-CChecks for memory comparison of floating-point values (rule fully covered)
PRQA QA-C

9.7

5026 
PVS-Studio

7.07

V1014
RuleChecker

20.10

memcmp-with-floatPartially checked
TrustInSoft Analyzer

1.38


Exhaustively verified.

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Bibliography

[ISO/IEC 9899:2011]Annex F, "IEC 60559 floating-point arithmetic"



  

6 Comments

  1. you actually should never use the "==" operator to compare floating point variables as the compliant solution suggests, as floating point numbers have limited precision on different machine types.

    1. That depends entirely on what you are comparing and why you are comparing it, so I would disagree with the statement that you should never use == for comparing floating-point values. The rule is about how to determine if two floats are equal, and == is the canonical way to do that test for equality. If you need to tell whether two floats are "equal enough" because you want to account for precision, rounding, etc, you should use other approaches depending on the expected precision of the floats involved (max ULPs, using an epsilon, etc).

      1. Sure, but it depends too on how s1->f and s2→f are valued. If one is a constant (for example) and other is calculated, there is a great chance that the equality test by '==' always evaluated to false.

        1. See recommendation FLP02-C. Avoid using floating-point numbers when precise computation is needed for discussion about using == on floating-point values vs. other approaches.


        2. Again, this depends on the domain. If you need that comparison to be equality in your domain, that result may be entirely correct. For instance, if the constant you are comparing against is 0.0, you may really care about the difference between 0.0 and "almost 0.0" depending on the problem being solved.

  2. I agree with all which is written here. So, we could specify those limitations and remember FLP02-C. Avoid using floating-point numbers when precise computation is needed .

    In order to avoid confusion.