Cast the return value of a function that returns a floating point type to ensure predictable program execution.
The C99 Section 6.8.6.4, para. 2, of the C standard [ISO/IEC 9899:19992011] states in Section 6.8.6.4, Paragraph 3:
If a return statement with an expression is executed, the value of the expression is returned to the caller as the value of the function call expression. If the expression has a type different from the return type of the function in which it appears, the value is converted as if by assignment to an object having the return type of the function.142
This paragraph is footnoted as is annotated (footnote 160) as follows:
142 The return statement is not an assignment. The overlap restriction of subclause 6.5.16.1 does not apply to the case of function return. The representation of floating-point values may have wider range or precision . A than implied by the type; a cast may be used to remove this extra range and precision.
Conversion as if by assignment to the type of the function is required if the return expression has a different type than the function, but not if the return expression has a wider value only because of wide evaluation. This allows seemingly inconsistent and confusing behavior. Consider the following code as an example:
Code Block |
---|
float f(float x) {
return x * 0.1f;
}
float g(float x) {
return x * 0.1;
}
|
...
Although the current text does not require narrowing return expressions of the same type as the function, it does not clearly state what is allowed. Is it allowed to narrow the result? Is it allowed to narrow the result sometimes but not always? Is it allowed to partially narrow the result (for example, if the ABI returns floats in double format, but a float function has a float return expression evaluated to wider than double)? An aggressive implementation could argue âyesâ yes to all these questions, though the resulting behavior would complicate debugging and error analysis.
Footnote 142 160 says a cast may be used to remove extra range and precision from the return expression. This means that a predictable program must have casts on all floating-point function calls (except where the function directly feeds an operator-like assignment that implies the conversion). With type-generic math (tgmath)
, the programmer has to reason through the tgmath
resolution rules to determine which casts to apply. These are significant obstacles to writing predictable code.
NOTE: WG14 has voted to include the following text in C1X. This only impacts implementations C11 [ISO/IEC 9899:2011]. It impacts only implementations that implement the optional Annex F, "IEC 60559 floating-point arithmetic."
...
This noncompliant code example fails to cast the result of the expression in the return statement and thereby guarantee that the range or precision is no wider than expected. The uncertainty in this example is introduced by the constant 0.1f
. This constant may be stored with a range or precision that is greater than that of float
. Consequently, the result of x * 0.1f
may also have a range or precision greater than that of float
. As described abovepreviously, this range or precision may not be reduced to that of a float
and, thus, so the caller of calcPercentage()
may receive a value that is more precise than expected. This may lead to inconsistent program execution across different platforms.
Code Block | ||||
---|---|---|---|---|
| ||||
float calcPercentage(float value) {
return value * 0.1f;
}
void floatRoutine(void) {
float value = 99.0f;
long double percentage;
percentage = calcPercentage(value);
}
|
...
This compliant solution casts the value of the expression in the return statement. This It forces the return value to have the expected range and precision, as described in Section 5.2.4.2.2 8 , para. 8, of the C Standardstandard [ISO/IEC 9899:2011].
Code Block | ||||
---|---|---|---|---|
| ||||
float calcPercentage(float value) {
return (float)(value * 0.1f);
}
void floatRoutine(void) {
float value = 99.0f;
long double percentage;
percentage = calcPercentage(value);
}
|
...
Code Block | ||||
---|---|---|---|---|
| ||||
float calcPercentage(float value) {
volatile float result;
result = value * 0.1f;
return result;
}
void floatRoutine(void) {
float value = 99.0f;
long double percentage;
percentage = calcPercentage(value);
}
|
...
Code Block | ||||
---|---|---|---|---|
| ||||
void floatRoutine(void) {
float value = 99.0f;
long double percentage;
percentage = (float)calcPercentage(value);
}
|
...
Code Block | ||||
---|---|---|---|---|
| ||||
void floatRoutine(void) {
float value = 99.0f;
long double percentage;
float temp;
percentage = temp = calcPercentage(value);
}
|
...
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
ISO/IEC 9899:19992011, Section 6.8.6.4, "The return
statement"
Bibliography
...