Ensure to have predictable outcomes in loops by using int instead of float
variables as a counter.
Floating-point arithmetic can only represent a finite subset of real numbers [IEEE Std 754-2019], such as 0.555....
represented by 0.5555555555555556
also discussed in CWE-1339: Insufficient Precision or Accuracy of a Real Number. Code examples in this rule are based on [Albing and Vossen, 2017].
Side effects of using float
as a counter is demonstrated in example01.py
showcasing that calculating 0.1 + 0.2
does not end up as 0.3
.
""" Code Example """
value = 0.0
while value <= 1:
print(f"{type(value)} {value}")
value += 0.1
Output of example01.py:
<class 'float'> 0.0
<class 'float'> 0.1
<class 'float'> 0.2
<class 'float'> 0.30000000000000004
<class 'float'> 0.4
<class 'float'> 0.5
<class 'float'> 0.6
<class 'float'> 0.7
<class 'float'> 0.7999999999999999
<class 'float'> 0.8999999999999999
<class 'float'> 0.9999999999999999
The noncompliant01.py
code demonstrates a side effect when a floating point counter is used.
""" Non-compliant Code Example """
counter = 0.0
while counter <= 1.0:
if counter == 0.8:
print("we reached 0.8")
break # never going to reach this
counter += 0.1
The noncompliant01.py
code will never print “we are at 0.8” due to lack of precision or controlled rounding.
The compliant01.py
makes use of integer as long as possible and only converts to float
where needed.
""" Compliant Code Example """
counter = 0
while counter <= 10:
value = counter/10
if value == 0.8:
print("we reached 0.8")
break
counter += 1
The use of range(10)
is more compact and prohibits float
.
Python float
has a limit in precision as demonstrated in example02.py
""" Code Example """
print(f"{1.0 + 1e-16:.20f}")
print(f"{1.0 + 1e-15:.20f}")
Output of example02.py:
1.00000000000000000000
1.00000000000000111022
Below noncompliant02.py
code tries to increment a floating-point COUNTER
by a too small value causing an infinite loop.
""" Non-compliant Code Example """
counter = 1.0 + 1e-16
target = 1.0 + 1e-15
while counter <= target: # never ends
print(f"counter={counter / 10**16 :.20f}")
print(f" target={target / 10**16:.20f}")
counter += 1e-16
The code will loop forever due to missing precision in the initial calculation of COUNTER = 1.0 + 1e-16
.
Use of an int
loop counter that is only converted to float
when required is demonstrated in compliant2.py
.
""" Compliant Code Example """
counter = 1
target = 10
while counter <= target:
print(f"counter={counter / 10**16 :.20f}")
print(f" target={target / 10**16:.20f}")
counter += 1
Definition | Explanation | Reference |
---|---|---|
Loop Counters | loop counters are variables used to control the iterations of a loop | Loop counter - Wikipedia |
Tool | Version | Checker | Description |
---|---|---|---|
Bandit | 1.7.4 on Python 3.10.4 | Not Available | |
Flake8 | 8-4.0.1 on Python 3.10.4 | Not Available |
MITRE CWE | Pillar CWE-664: Improper Control of a Resource Through its Lifetime (4.13) (mitre.org) |
MITRE CWE | Class CWE-197: Numeric Truncation Error |
SEI CERT Coding Standard for Java | NUM09-J. Do not use floating-point variables as loop counters |
SEI CERT C Coding Standard | FLP30-C. Do not use floating-point variables as loop counters |
[ISO/IEC TR 24772:2019] | Programming languages — Guidance to avoiding vulnerabilities in programming languages, available from https://www.iso.org/standard/71091.html |
IEEE Std 754-2019 | IEEE Standard for Floating-Point Arithmetic, available from: https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=8766229, [Last accessed June 2024] |
[Wikipedia 2024] | Repeating Decimals, available from:https://en.wikipedia.org/wiki/Repeating_decimal, [Last accessed August 2024] |
[Albing and Vossen, 2017] | Albin, C. and Vossen, JP (2017) 6.13 Looping with Floating Point Values. In: Bleiel, J., Brown, K. and Head, R. eds. bash Cookbook: Solutions and Examples for bash Users, 2d Edition. Sebastopol: O’Reilly Media, Inc., pp.159-160 |
[Bloch 2005] | Puzzle 34, “Down for the Count”, available from: https://web.archive.org/web/20220511061752/https://wiki.sei.cmu.edu/confluence/display/java/Rule+AA.+References#RuleAA.References-Bloch05, [Last accessed August 2024] |