CertC++-FLP37

Do not use object representations to compare floating-point values

Required inputs: IR

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
Bibliography
[ ISO/IEC 9899:2011] Annex F, "IEC 60559 floating-point arithmetic"
Excerpt from SEI CERT C++ Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/rules/floating-point-flp/flp37-c], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

memcmp_char_pointer_arg

memcmp shall not be used with char pointer argument, use strncmp instead.

None

False

memcmp_float

memcmp shall not be used to compare floats as the same value may be stored using different representations.

None

False

Options

allow_char

allow_char : bool = True

Whether to allow memcmp on char type.
 

allow_composites_without_padding

allow_composites_without_padding : bool = True

Whether to allow using memcmp on structs and unions that have no padding bytes.
 

allow_float

allow_float : bool = False

Whether to allow memcmp on floating point types.
 

functions

functions : set[bauhaus.analysis.config.QualifiedName] = {'memcmp'}

Names of functions being relevant as call targets for this check.
 

ignore_calls_in_functions

ignore_calls_in_functions : set[bauhaus.analysis.config.QualifiedName] = set()

Qualified names of function definitions in which calls to relevant functions are ignored for this check.