CertC++-FLP34¶
Ensure that floating-point conversions are within range of the new type
Required inputs: IR
If a floating-point value is to be converted to a floating-point value of a smaller range and precision or to an integer type, or if an integer type is to be converted to a floating-point type, the value must be representable in the destination type.
The C Standard, 6.3.1.4, paragraph 1 [ ISO/IEC 9899:2011], says,
When a finite value of real floating type is converted to an integer type other than
_Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.
Paragraph 2 of the same subclause says,
When a value of integer type is converted to a real floating type, if the value being converted can be represented exactly in the new type, it is unchanged. If the value being converted is in the range of values that can be represented but cannot be represented exactly, the result is either the nearest higher or nearest lower representable value, chosen in an implementation-defined manner. If the value being converted is outside the range of values that can be represented, the behavior is undefined.
And subclause 6.3.1.5, paragraph 1, says,
When a value of real floating type is converted to a real floating type, if the value being converted can be represented exactly in the new type, it is unchanged. If the value being converted is in the range of values that can be represented but cannot be represented exactly, the result is either the nearest higher or nearest lower representable value, chosen in an implementation-defined manner. If the value being converted is outside the range of values that can be represented, the behavior is undefined.
See undefined behaviors 17 and 18.
This rule does not apply to demotions of floating-point types on implementations that support signed infinity, such as IEEE 754, as all values are within range.
Noncompliant Code Example (
float to
int)
This noncompliant code example leads to undefined behavior if the integral part
of
f_a cannot be represented as an integer:
void func(float f_a) {
int i_a;
/* Undefined if the integral part of f_a cannot be represented. */
i_a = f_a;
}
Compliant Solution (
float to
int)
This compliant solution tests to ensure that the
float value will fit within the
int variable before performing the assignment.
#include <float.h>
#include <limits.h>
#include <math.h>
#include <stddef.h>
#include <stdint.h>
extern size_t popcount(uintmax_t); /* See INT35-C */
#define PRECISION(umax_value) popcount(umax_value)
void func(float f_a) {
int i_a;
if (isnan(f_a) ||
PRECISION(INT_MAX) < log2f(fabsf(f_a)) ||
(f_a != 0.0F && fabsf(f_a) < FLT_MIN)) {
/* Handle error */
} else {
i_a = f_a;
}
}
Noncompliant Code Example (Narrowing Conversion)
This noncompliant code example attempts to perform conversions that may result in truncating values outside the range of the destination types:
void func(double d_a, long double big_d) {
double d_b = (float)big_d;
float f_a = (float)d_a;
float f_b = (float)big_d;
}
As a result of these conversions, it is possible that
d_a is outside the range of values that can be represented by a
float or that
big_d is outside the range of values that can be represented as
either a
float or a
double. If this is the case, the result is undefined on
implementations that do not support Annex F, "IEC 60559 Floating-Point
Arithmetic."
Compliant Solution (Narrowing Conversion)
This compliant solution checks whether the values to be stored can be represented in the new type:
#include <float.h>
#include <math.h>
void func(double d_a, long double big_d) {
double d_b;
float f_a;
float f_b;
if (d_a != 0.0 &&
(isnan(d_a) ||
isgreater(fabs(d_a), FLT_MAX) ||
isless(fabs(d_a), FLT_MIN))) {
/* Handle error */
} else {
f_a = (float)d_a;
}
if (big_d != 0.0 &&
(isnan(big_d) ||
isgreater(fabs(big_d), FLT_MAX) ||
isless(fabs(big_d), FLT_MIN))) {
/* Handle error */
} else {
f_b = (float)big_d;
}
if (big_d != 0.0 &&
(isnan(big_d) ||
isgreater(fabs(big_d), DBL_MAX) ||
isless(fabs(big_d), DBL_MIN))) {
/* Handle error */
} else {
d_b = (double)big_d;
}
}
Risk Assessment
Converting a floating-point value to a floating-point value of a smaller range and precision or to an integer type, or converting an integer type to a floating-point type, can result in a value that is not representable in the destination type and is undefined behavior on implementations that do not support Annex F.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| FLP34-C | Low | Unlikely | Low | P3 | L3 |
Related Guidelines
| Taxonomy | Taxonomy item | Relationship |
|---|---|---|
| CERT Oracle Secure Coding Standard for Java | NUM12-J. Ensure conversions of numeric types to narrower types do not result in lost or misinterpreted data | Prior to 2018-01-12: CERT: Unspecified Relationship |
| ISO/IEC TR 24772:2013 | Numeric Conversion Errors [FLC] | Prior to 2018-01-12: CERT: Unspecified Relationship |
| CWE 2.11 | CWE-681, Incorrect Conversion between Numeric Types | 2017-06-29: CERT: Rule subset of CWE |
| CWE 2.11 | CWE-197 | 2017-06-14: CERT: Rule subset of CWE |
Bibliography
| [ IEEE 754 2006] | |
| [ ISO/IEC 9899:2011] | Subclause 6.3.1.4, "Real Floating and Integer" Subclause 6.3.1.5, "Real Floating Types" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
cast_changes_type_category |
Ensure that floating-point conversions are within range of the new type. |
None |
False |
narrowing_cast |
Ensure that floating-point conversions are within range of the new type. |
None |
False |
widening_cast |
Conversion to larger type |
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
category_changes¶
category_changes
List of (from, to) type category pairs to check for. Both from and to are sets of categories.Type: list[typing.Tuple[typing.Set[bauhaus.ir.common.types.type_systems.TypeCategory], typing.Set[bauhaus.ir.common.types.type_systems.TypeCategory]]]
Default:
[({'float_types'}, {'bool_types', 'char_types', 'enum_types', 'float_types', 'function_pointer_types', 'incomplete_pointer_types', 'null_pointer_types', 'object_pointer_types', 'other_types', 'signed_types', 'unsigned_types', 'void_pointer_types', 'void_types'}), ({'bool_types', 'char_types', 'enum_types', 'float_types', 'function_pointer_types', 'incomplete_pointer_types', 'null_pointer_types', 'object_pointer_types', 'other_types', 'signed_types', 'unsigned_types', 'void_pointer_types', 'void_types'}, {'float_types'})]
check_explicit_casts¶
check_explicit_casts : bool = True
check_implicit_casts¶
check_implicit_casts : bool = True
look_through_casts¶
look_through_casts : bool = False
only_complex_expressions¶
only_complex_expressions : bool = False
report_narrowing¶
report_narrowing : bool = True
report_widening¶
report_widening : bool = False
show_operand_in_entity¶
show_operand_in_entity : bool = False
size_source_category¶
size_source_category
Selection of type categories of the operand being cast.Type: set[TypeCategory]
Default:
{'float_types', 'signed_types', 'unsigned_types'}
size_target_category¶
size_target_category
Selection of type categories appearing as target types of the cast.Type: set[TypeCategory]
Default:
{'float_types', 'signed_types', 'unsigned_types'}
type_system¶
type_system : bauhaus.ir.common.types.type_systems.TypeSystem = <bauhaus.ir.common.types.type_systems.CompilerTypeSystem object at 0x7f6f1c5fd510>
Option Types¶
These types are used by options listed above:
TypeCategory¶
Base class for the different type categories.signed_types
unsigned_types
float_types
char_types
plain char.bool_types
bool, _Bool and special expressions.enum_types
void_types
void_pointer_types
incomplete_pointer_types
function_pointer_types
object_pointer_types
null_pointer_types
other_types
Those not covered by other categories.