
When using binary operators with mixed operand sizes, be aware that some of the narrower operands may be promoted to a wider type, to match the type of the other operand. For example in the expression 'a' == 42
, the field 'a'
which is shorter than an int
will be promoted to an int
before the comparison is carried out.
According to [[JLS 05]]:
If an integer operator other than a shift operator has at least one operand of type long, then the operation is carried out using 64-bit precision, and the result of the numerical operator is of type long. If the other operand is not long, it is first widened (§5.1.5) to type long by numeric promotion (§5.6). Otherwise, the operation is carried out using 32-bit precision, and the result of the numerical operator is of type int. If either operand is not an int, it is first widened to type int by numeric promotion.
The Java Language Specification [[JLS 05]] section 5.6 "Numeric Promotions" describes numeric promotion as:
- If any of the operands is of a reference type, unboxing conversion is performed. Then:
- If either operand is of type double, the other is converted to double.
- Otherwise, if either operand is of type float, the other is converted to float.
- Otherwise, if either operand is of type long, the other is converted to long.
- Otherwise, both operands are converted to type int.
Widening conversions resulting from integer promotions preserve the overall magnitude of the number. However, promotions in which the operands are converted from a numeric type such as an integer
to a float
or a long
to a double
, are particularly pernicious (see INT33-J. Be careful while casting numeric types to wider floating-point types for more details). These implicit casts can lead to an undesirable loss in precision.
These conversions can happen with the following operators : multiplicative operators (%, *, /), additive operators (+, -), comparison operators (<, >, <=, >=) and equality (==, !=) and the integer bitwise operators (&, |, ^).
In the following example, a
is promoted to a double
before the +
operator is applied.
int a = some_value; double b = some_other_value; double c = a + b;
As another example, consider:
int a = some_value; char b = some_character; if((a + b) > 1.1f) { //do something }
Here, b
is first converted to int
so that the +
operator can be applied to operands of the same type. The result of (a+b)
is then converted to a float
, and the comparison operator is finally applied.
Noncompliant Code Example
In this noncompliant code example, the statement big * one
carries out a binary operation. As big
is an int
and one
is of type float
, big
is promoted to a float
. This implicit case results in loss of precision.
class Test{ public static void main(String[] args){ int big = 1999999999; float one = 1.0f; System.out.println(big*one); // binary operation, loses precision due to implicit cast double big_float = big; System.out.println(big_float); // double is a wider type, preserves precision } }
The output is:
2.0E9 // loss of precision 1.999999999E9
Whereas, the expected output is:
1.999999999E9 1.999999999E9
Compliant solution
In this case, a double
should be used instead of a float
for a safe widening primitive conversion caused by integer promotion.
class Test{ public static void main(String[] args){ int big = 1999999999; double one = 1.0d; // double instead of float System.out.println(big*one); double big_float = big; System.out.println(big_float); } }
The output is as expected :
1.999999999E9 1.999999999E9
Risk assessment
Failing to consider integer promotions while dealing with floating point and integer operands together, can result in loss of precision.
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
EXP08-J |
low |
probable |
medium |
P4 |
L3 |
References
[[JLS 05]] 4.2.2 "Integer Operations" and 5.6 "Numeric Promotions"