...
Code Block |
---|
float f(float x) { return x * 0.1f; } float g(float x) { return x * 0.1; } |
Function f()
is allowed to return a value wider than float
, but function g()
(which uses the wider constant) is not.
Although the standard 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 application binary interface [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 to all these questions, though the resulting behavior would complicate debugging and error analysis.
Footnote 160 in the C Standard 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 floatingfunction calls that return floating-point function calls values (except where the function directly feeds an operator-like assignment that implies the conversion). With type-generic math (tgmath.h)
, the programmer has to reason through the tgmath.h
resolution rules to determine which casts to apply. These are significant obstacles to writing predictable code.
...
If the return expression is evaluated in a floating-point format different from the return type, the expression is converted as if by assignment362 to the return type of the function and the resulting value is returned to the caller.
362) Assignment removes any extra range and precision.
It impacts only implementations that implement This applies only to implementations that conforms to the optional Annex F, "IEC 60559 Floating-Point Arithmetic." The macro __STDC_IEC_559__
can be used to determine whether an implementation conforms to Annex F.
...
Code Block | ||||
---|---|---|---|---|
| ||||
float calcPercentage(float value) { return (float)(value * 0.1f); } void floatRoutine(void) { float value = 99.0f; long double percentage; percentage = calcPercentage(value); } |
Compliant Solution (
...
Within the Function
...
)
Forcing the range and precision inside the calcPercentage()
function is a good way to fix the problem once without having to apply fixes in multiple locations (every time calcPercentage()
is called). However, access to the called function may not always be available. This compliant solution shows one way to force the correct range and precision in a situation in which the source of the called function cannot be modified. This behavior is accomplished by casting the return value of the calcPercentage()
function to float
.uses a temporary variable as the forcing mechanism:
Code Block | ||||
---|---|---|---|---|
| ||||
void floatRoutine(void) { float value = 99.0f; long double percentage; volatile float temp; percentage = temp = (float)calcPercentage(value); } |
Compliant Solution (Outside the Function
...
)
Source code to the called function may not always be available. This compliant solution shows another way casts the return value of the calcPercentage()
function to float
to force the correct range and precision . In this case, a temporary variable is used as the forcing mechanismwhen the source of the called function cannot be modified.
Code Block | ||||
---|---|---|---|---|
| ||||
void floatRoutine(void) { float value = 99.0f; long double percentage; volatile float temp; percentage = temp = (float)calcPercentage(value); } |
Risk Assessment
...