Working with string representations of floating-point values can produce incorrect conclusions about the precision of the values. For example, consider the conversion of a value from type float
to type double
, a widening primitive conversion. Refer to the rule "NUM17-J. Beware of precision loss when converting primitive integers to floating-point" for more details about such conversions.
String representations of floating-point numbers shall should not be compared or inspected. When the value of a float
variable must be represented exactly using the double
type, an explicit assignment is more appropriate than first converting the floating-point value to a String
and then to a double
If they are used, significant care needs to be taken to ensure expected behavior.
Noncompliant Code Example (String Comparison)
This noncompliant code example attempts a string-based comparison of a floating-point number.
Code Block | ||
---|---|---|
| ||
int i = 1;
String s = Double.valueOf(i / 1000.0).toString();
if (s.equals("0.001")) {
// ...
}
|
However s
actually contains the string "0.0010"
. Consequently, the comparison unexpectedly fails.
Noncompliant Code Example (Regex)
This noncompliant code example attempts to mitigate the extra trailing zero by using a regular expression on the string before comparing it.
Code Block | ||
---|---|---|
| ||
int i = 1;
String s = Double.valueOf(i / 1000.0).toString();
s = s.replaceFirst("[.0]*$", "");
if (s.equals("0.001")) {
// ...
}
|
While the comparison does succeed on the code above, it fails on the similar code below, which uses 1/10000.0
instead of 1/1000.0
. incorrectly compares the decimal string literal generated by 1/10000.0
. The string produced is not 0.00010
but 0001
but rather 1.0E-4
.
Code Block | ||
---|---|---|
| ||
int i = 1; String s = Double.valueOf(i / 10000.0).toString(); s = s.replaceFirst("[.0]*$", ""); if (s.equals("0.0001")) { // ... } |
Compliant Solution (String Comparison)
This compliant solution uses the BigDecimal
class to avoid precision lossthe conversion into scientific notation. It then performs a numeric comparison, which passes as expected.
Code Block | ||
---|---|---|
| ||
int i = 1; BigDecimal d = new BigDecimal(Double.valueOf(i / 100010000.0).toString()); if (d.compareTo(new BigDecimal("0.0010001")) == 0) { // ... } |
Risk Assessment
Relying on Comparing or inspecting the string representation of floating-point types can result in imprecise valuesvalues may have unexpected results.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|
NUM11-J |
Low |
Likely |
Medium | P6 | L2 |
Automated Detection
TODO
Related Vulnerabilities
Bibliography
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="1193217f-c33e-4523-96d8-61f012036e90"><ac:plain-text-body><![CDATA[ | [[API 2006 | AA. Bibliography#API 06]] | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="c6ec803f-2d5d-49a6-81c9-075868003b4f"><ac:plain-text-body><![CDATA[ | [[JLS 2005 | AA. Bibliography#JLS 05]] | ]]></ac:plain-text-body></ac:structured-macro> |
Android Implementation Details
Comparing or inspecting the string representation of floating-point values may have unexpected results on Android.
Bibliography
...
NUM13-J. Do not construct BigDecimal objects from floating-point literals 03. Numeric Types and Operations (NUM) NUM15-J. Ensure conversions of numeric types to narrower types do not result in lost or misinterpreted data