
When using binary operators with mixed operand sizes, be aware that some of the narrower operands could 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 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 typelong
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.
Promotion Rules
The JLS Section 5.6 "Numeric Promotions" describes numeric promotion as the following:
- 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 an int
to a float
or from a long
to a double
can cause unexpected loss of precision. (See guideline FLP10-J. Beware of precision lost when casting primitive integers to floating-point for more details.)
These conversions can happen with the following operators: multiplicative operators (%, *, /), additive operators (+
, -
), comparison operators (<
, >
, <=
, >=
), equality operators (==
, !=
) and the integer bitwise operators (&
, |
, ^
).
Examples
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;
Also consider this example:
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.
Compound Operators
Sometimes invisible implicit casts occur when compound expressions are used with mixed operand types. Examples of compound assignment operators are +=
, -=
, *=
, /=
, &=
, ^=
, %=
, <<=
, >>=
, >>>=
and |=
.
According to the JLS Section 15.26.2, Compound Assignment Operators
A compound assignment expression of the form
E1 op= E2
is equivalent toE1 = (T)((E1) op (E2))
, whereT
is the type ofE1
, except thatE1
is evaluated only once.
That is, the compound assignment expression implicitly casts the resulting computation to the type of the left-hand operand.
One or more implicit invisible conversions occur when the operands are different types. When E1
is an int
and E2
is either a long
, a float
, or a double
, for example, there will be two conversions: first, a widening conversion on E1
from int
to the type of E2
(before the "op"), and, second, a narrowing conversion from the type of E2
back to int
(after the "op" and before the assignment).
Noncompliant Code Example (Multiplication)
In this noncompliant code example, the statement big * one
carries out a binary operation. Because big
is type int
, and one
is type float
, big
is promoted to the type 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 because of implicit cast System.out.println(big * one); } }
This code outputs 2.0E9 whereas, the expected output is 1.999999999E9.
Compliant Solution (Multiplication)
This compliant solution uses the double
type, instead of float
, as a safer means of handling the widening primitive conversion resulting from 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 expected output of 1.999999999E9. This is the value that is obtained when an int
is assigned (implicitly cast) to a double
.
Noncompliant Code Example (Left Shift)
This noncompliant code example shows integer promotion resulting from the use of the bit-wise OR operator. The byte array element is sign extended to 32 bits before it is used as an operand. If it originally contained the value 0xff
, it would contain 0xffffffff
[[Findbugs 2008]].
int result = 0; for(int i = 0; i < 4; i++) result = ((result << 8) | b[i]);
Compliant Solution (Left Shift)
This compliant solution continues to use integer promotion, but it masks off the upper 24 bits of the byte array element to achieve the intended result.
int result = 0; for(int i = 0; i < 4; i++) result = ((result << 8) | (b[i] & 0xff));
Noncompliant Code Example (Compound Addition & Assignment)
This noncompliant code example performs a compound assignment operation. This operation involves an int
value that contains too many significant bits to fit in the twenty-three bit mantissa of a Java float
, causing the widening conversion from int
to float
to lose precision. The resulting value could surprise many programmers.
public class Expr { public static void main(String[] args) { int x = 2147483642; // 0x7ffffffa x += 1.0f; // x contains 2147483647 (0x7fffffff) after the computation } }
Compliant Solution (Compound Addition & Assignment)
To be safe, avoid using any of the compound assignment operators on variables of types byte
, short
or char
. Also, refrain from using a wider operand on the right hand side. In this compliant solution, all operands are of the Java type double
.
public class Expr { public static void main(String[] args) { double x = 2147483642; // 0x7ffffffa x += 1.0; // x contains 2147483643.0 (0x7ffffffb.0) as expected } }
Noncompliant Code Example (Compound Bit Shift & Assignment)
This noncompliant code example uses a compound right shift operator for shifting the value of i
, bit by bit. Unfortunately, the value of i
remains the same. The value of i
is first promoted to an int
. This is a widening primitive conversion, so no data is lost. With short
, -1
is represented as 0xffff
. The conversion to int
results in the value 0xffffffff
, which is right shifted by 1 bit to yield 0x7fffffff
. To store the value back into the short
variable i
, Java then performs an implicit narrowing conversion, discarding the 16 higher order bits. The final result is again 0xffff
or -1
.
short i = -1; i >>>= 1;
Compliant Solution (Compound Bit Shift & Assignment)
This compliant solution applies the compound assignment operator to an int
which does not require widening and subsequent narrowing.
int i = -1; i >>>= 1;
Risk Assessment
Failing to consider integer promotions when dealing with floating point and integer operands can result in loss of precision.
Guideline |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
INT10-J |
low |
probable |
medium |
P4 |
L3 |
Automated Detection
Automated detection of numeric promotion is straightforward. Determining which promotions may be problematic is infeasible in the general case. A heuristic approach may provide acceptable results.
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this guideline on the CERT website.
Bibliography
[[Bloch 2005]] Puzzle 9: "Tweedledum," and Puzzle 31: "Ghost of Looper"
[[Findbugs 2008]] "BIT: Bitwise OR of signed byte value"
[[JLS 2005]] Section 4.2.2, "Integer Operations", Section 5.6
, "Numeric Promotions", and Section 15.26.2
, "Compound Assignment Operators"
Expressions (EXP) EXP06-J. Use parentheses for precedence of operation