Loop counter declared with a floating-point type
ID |
objectivec.correctness.float_loop_counter |
Severity |
low |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Numeric Precision |
Language |
Objective-C |
Description
Reports a for loop whose counter is declared in the loop header with a floating-point type - float, double, CGFloat or NSTimeInterval. A loop over a pointer to a floating-point element, an integer counter, a fast-enumeration loop, and a copy of the shape inside a comment are not reported.
Rationale
Decimal step values such as 0.1 have no exact binary representation, so each iteration adds a slightly wrong increment and the error accumulates. The number of iterations then depends on rounding rather than on the arithmetic the header appears to describe: a loop from 0.0 to 1.0 in steps of 0.1 runs ten times on one platform and eleven on another, and the last value is not 1.0 but something a few ULPs away from it. Any equality test against the counter is unreliable for the same reason, and a loop written as t != limit may not terminate at all.
The following code illustrates the pattern detected by this rule:
@implementation Sampler
- (void)sampleUnitInterval {
// FLAGGED: Loop counter declared with a floating-point type
for (float t = 0.0f; t < 1.0f; t += 0.1f) {
[self sampleAt:t];
}
}
- (void)sampleAt:(double)t { }
- (void)rotateBy:(double)angle { }
- (void)poll { }
@end
Remediation
Count with an integer and derive the floating-point value inside the body - for (NSInteger i = 0; i ⇐ 10; i++) { CGFloat t = i * 0.1; }. The iteration count is then exact and stated in the header, and the accumulated rounding error disappears because each value is computed from the index rather than from the previous value.