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" |
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¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
allow_char¶
allow_char : bool = True
memcmp on char type.
allow_composites_without_padding¶
allow_composites_without_padding : bool = True
memcmp on structs and unions that have no
padding bytes.
allow_float¶
allow_float : bool = False
memcmp on floating point types.
functions¶
functions : set[bauhaus.analysis.config.QualifiedName] = {'memcmp'}
ignore_calls_in_functions¶
ignore_calls_in_functions : set[bauhaus.analysis.config.QualifiedName] = set()