String representations of floating point values may lead to incorrect conclusions about the precision of the values. For example, consider converting a value of type float
to the type double
, a widening primitive conversion. Refer to the guideline INT03-J. Avoid casting primitive integer types to floating-point types without range checks for more details about such conversions. If the value of the 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
-point numbers should not be compared or inspected. If they are used, significant care needs to be taken to ensure expected behavior.
Noncompliant Code Example (String Comparison)
This noncompliant code example converts a value of type float
to a value of type double
. The float
variable contains the equivalent of 1/3 (0.3333333432674408). However, the default string representation can mislead a programmer into thinking that the correct value is 0.33333334. Consequently, the double
variable may acquire an imprecise value. The imprecise value arises from the default precision used for converting floating point numbers to a string.
Code Block | ||
---|---|---|
| ||
double d;
float f = 1/3f; // Contains the value 0.33333334
d = Double.valueOf(String.valueOf(f)); // Now contains the value 0.33333334
|
Compliant Solution
This compliant solution assigns the value of type float
to the double
variable directly.
Code Block | ||
---|---|---|
| ||
double d;
float f = 1/3f; // Contains the value 0.3333333432674408
d = f; // Assigned the value 0.3333333432674408
|
Noncompliant Code Example
This noncompliant code example obtains the result of 1/1000.0 and represents it as a String
. However, even though the resulting value is precise, an extra zero digit is appended at the end. Any operations on the string, such as comparisons, may yield incorrect results.incorrectly compares the decimal string literal generated by 1/10000.0
. The string produced is not 0.0001
but rather 1.0E-4
.
Code Block | ||
---|---|---|
| ||
int i = 1; String s = Double.valueOf (i / 100010000.0).toString(); // s contains 0.0010 if (s.equals("0.0010001")) { // Fails // Do something } |
Noncompliant Code Example
...
. |
...
. |
...
. |
...
}
|
Compliant Solution (String Comparison)
Code Block | ||
---|---|---|
| ||
int i = 1;
String s = Double.valueOf (i / 10000.0).toString(); // s contains 0.0010
s = s.replaceFirst("[.0]*$", "");
// ...
|
...
This compliant solution uses the BigDecimal
class and strips the trailing zeros so that future operations do not failto avoid the 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()).stripTrailingZeros(); if (d.compareTo(new BigDecimal("0.0010001")) == 0) { // Passes // Do something... } |
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
Wiki Markup |
---|
\[[API 2006|AA. Bibliography#API 06]\]
\[[JLS 2005|AA. Bibliography#JLS 05]\] |
Android Implementation Details
Comparing or inspecting the string representation of floating-point values may have unexpected results on Android.
Bibliography
...
FLP08-J. Avoid using floating point literals with the BigDecimal constructor 07. Floating Point (FLP) 08. Object Orientation (OBJ)