|
| 1 | +# CWE-681: Avoid an uncontrolled loss of precision when passing floating-point literals to a Decimal constructor |
| 2 | + |
| 3 | +When working with decimal numbers in Python, using floating-point literals as input to the `Decimal` constructor can lead to unintended imprecision due to the limitations of `IEEE 754` [Wikipedia 2025](https://en.wikipedia.org/wiki/IEEE_754) floating-point representation; therefore, to ensure accurate decimal representation, it is advisable to avoid using floating-point literals. |
| 4 | + |
| 5 | +## Noncompliant Code Example |
| 6 | + |
| 7 | +In the `noncompliant01.py` code example, a floating-point value is given to the Decimal constructor. The decimal `0.45` cannot be exactly represented by a floating-point literal, and hence the output of the decimal is imprecise. This is because `0.45` as a floating-point representation deals with the number in binary. In binary, many decimal fractions cannot be represented exactly. As a result, `0.45` is stored as an approximate binary fraction in a `float`, and when this approximation is converted to a Decimal, the inexactness is preserved, can result in `0.450000000000000011102230246251565404236316680908203125`. |
| 8 | + |
| 9 | +*[noncompliant01.py](noncompliant01.py):* |
| 10 | + |
| 11 | +```py |
| 12 | +"""Non-compliant Code Example""" |
| 13 | + |
| 14 | +from decimal import Decimal |
| 15 | + |
| 16 | +print(Decimal(0.45)) |
| 17 | +``` |
| 18 | + |
| 19 | +## Compliant Solution |
| 20 | + |
| 21 | +In the `compliant01.py` code example, the floating-point value is passed as a string, allowing the value to be directly converted to a `Decimal` object with the exact decimal value. This is because the string representation is interpreted exactly as it appears, maintaining all the specified digits. |
| 22 | + |
| 23 | +*[compliant01.py](compliant01.py):* |
| 24 | + |
| 25 | +```py |
| 26 | +"""Compliant Code Example""" |
| 27 | + |
| 28 | +from decimal import Decimal |
| 29 | + |
| 30 | +print(Decimal("0.45")) |
| 31 | +``` |
| 32 | + |
| 33 | +## Related Guidelines |
| 34 | + |
| 35 | +||| |
| 36 | +|:---|:---| |
| 37 | +|[SEI CERT Oracle Coding Standard for Java](https://wiki.sei.cmu.edu/confluence/display/java/SEI+CERT+Oracle+Coding+Standard+for+Java?src=breadcrumbs)|[NUM10-J. Do not construct BigDecimal objects from floating-point literals](https://wiki.sei.cmu.edu/confluence/display/java/NUM10-J.+Do+not+construct+BigDecimal+objects+from+floating-point+literals)| |
| 38 | +|[MITRE CWE Base](http://cwe.mitre.org/)| [CWE-681](https://cwe.mitre.org/data/definitions/681.html)| |
| 39 | +|[MITRE CWE Pillar](http://cwe.mitre.org/)|[CWE-664: Improper Control of a Resource Through its Lifetime](https://cwe.mitre.org/data/definitions/664.html)| |
| 40 | + |
| 41 | +## Automated Detection |
| 42 | + |
| 43 | +|Tool|Version|Checker|Description| |
| 44 | +|:----|:----|:----|:----| |
| 45 | +|[Bandit](https://bandit.readthedocs.io/en/latest/)|1.7.4 on python 3.10.4|Not Available|| |
| 46 | +|[Flake8](https://flake8.pycqa.org/en/latest/)|8-4.0.1 on python 3.10.4|Not Available|| |
| 47 | + |
| 48 | +## Biblography |
| 49 | + |
| 50 | +||| |
| 51 | +|:---|:---| |
| 52 | +|[Wikipedia 2025](en.wikipedia.org)|IEEE 754 [online]. Available from: [https://en.wikipedia.org/wiki/IEEE_754](https://en.wikipedia.org/wiki/IEEE_754)| |
| 53 | +|[Python docs](https://docs.python.org/3/)|decimal — Decimal fixed-point and floating-point arithmetic [online]. Available from: [https://docs.python.org/3/library/decimal.html](https://docs.python.org/3/library/decimal.html) [accessed 2 February 2025]| |
0 commit comments