
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 the Java Language Specification [[JLS 05]], section 4.2.2 "Integer Operations":
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 typelong
. If the other operand is notlong
, 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 typeint
. If either operand is not anint
, it is first widened to typeint
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 int
to a float
or a long
to a double
, are particularly pernicious (see INT33-J. Do not cast numeric types to wider floating-point types without range checking for more details). These implicit casts can lead to loss of 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.
Also, see EXP04-J. Be wary of invisible implicit casts when using compound assignment operators and EXP05-J. Be careful of autoboxing when removing elements from a Collection.
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 cast results in loss of precision.
class Test{ public static void main(String[] args){ int big = 1999999999; float one = 1.0f; // binary operation, loses precision due to implicit cast System.out.println(big*one); } }
The output produced is 2.0E9 whereas, the expected output is 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); } }
This produces the required output of 1.999999999E9. This is the value that is obtained when an int
is assigned (implicitly cast) to a double
.
Noncompliant Code Example
This noncompliant code example shows integer promotion when the bit-wise OR operator is used. The byte array element is sign extended to 32 bits before it is used as an operand. If it contained the value 0xff
, now it would contain 0xffffffff
[[Findbugs 08]].
int result = 0; for(int i = 0; i < 4; i++) result = ((result << 8) | b[i]);
Compliant Solution
This compliant solution does not avoid integer promotion but works around it by masking off the upper 24 bits of the byte array element.
int result = 0; for(int i = 0; i < 4; i++) result = ((result << 8) | (b[i] & 0xff));
Risk Assessment
Failing to consider integer promotions when dealing with floating point and integer operands 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"
[[Findbugs 08]] "BIT: Bitwise OR of signed byte value"
EXP07-J. Do not diminish the benefits of constants by assuming their values in expressions 04. Expressions (EXP) EXP09-J. Use parentheses for precedence of operation