...
Left- and right-shift operators are often employed to multiply or divide a number by a power of two. This compromises code readability and portability for the sake of often-illusory speed gains. The Java Virtual Machine (JVM) usually makes such optimizations automatically, and, unlike a programmer, the JVM can optimize for the implementation details of the current platform. This noncompliant code example includes both bitwise and arithmetic manipulations of the integer x
that conceptually contains a numeric value. The result is a prematurely optimized statement that assigns the value 5x + 1
to x
, which is what the programmer intended to express.
Code Block | ||
---|---|---|
| ||
int x = 50;
x += (x << 2) + 1;
|
...
This noncompliant code example segregates arithmetic and bitwise operators by variables. The x
variable participates only in bitwise operations, and y
participates only in arithmetic operations.
Code Block | ||
---|---|---|
| ||
int x = 50;
int y = x << 2;
y += y + 1;
|
...
In this compliant solution, the assignment statement is modified to reflect the arithmetic nature of x
, resulting in a clearer indication of the programmer's intentions.
Code Block | ||
---|---|---|
| ||
int x = 50;
x = 5 * x + 1;
|
...
In this noncompliant code example, the programmer wishes to divide x
by 4. In a misguided attempt to optimize performance, the programmer uses a right-shift operation rather than a division operation.
Code Block | ||
---|---|---|
| ||
int x = -50;
x >>>= 2;
|
The >>>=
operator is a logical right shift; it fills the leftmost bits with zeroes, regardless of the number's original sign. After execution of this code sequence, x
contains a large positive number (specifically, 0x3FFFFFF3
). Using logical right shift for division produces an incorrect result when the dividend (x
in this example) contains a negative value.
...
In this noncompliant code example, the programmer attempts to correct the previous example by using an arithmetic right shift (the >>=
operator):
Code Block | ||
---|---|---|
| ||
int x = -50;
x >>= 2;
|
After this code sequence is run, x
contains the value -13
rather than the expected -12
. Arithmetic right shift truncates the resulting value towards negative infinity, whereas integer division truncates toward zero.
...
In this compliant solution, the right shift is replaced by division.
Code Block | ||
---|---|---|
| ||
int x = -50;
x /= 4;
|
Noncompliant Code Example
In this noncompliant code example, a programmer is attempting to fetch four values from a byte array and pack them into the integer variable result
. The integer value in this example represents a bit collection, not a numeric value.
Code Block | ||
---|---|---|
| ||
// b[] is a byte array, initialized to 0xff
byte[] b = new byte[] {-1, -1, -1, -1};
int result = 0;
for (int i = 0; i < 4; i++) {
result = ((result << 8) + b[i]);
}
|
...
This noncompliant code example masks off the upper 24 bits of the promoted byte array element before performing the addition. The number of bits required to mask the sizes of byte
and int
are specified by the JLS. Although this code calculates the correct result, it violates this rule by combining bitwise and arithmetic operations on the same data.
Code Block | ||
---|---|---|
| ||
byte[] b = new byte[] {-1, -1, -1, -1};
int result = 0;
for (int i = 0; i < 4; i++) {
result = ((result << 8) + (b[i] & 0xff));
}
|
...
This compliant solution masks off the upper 24 bits of the promoted byte array element. The result is then combined with result
using a logical OR operation.
Code Block | ||
---|---|---|
| ||
byte[] b = new byte[] {-1, -1, -1, -1};
int result = 0;
for (int i = 0; i < 4; i++) {
result = ((result << 8) | (b[i] & 0xff));
}
|
...
NUM01-EX0: Bitwise operations may be used to construct constant expressions.
Code Block | ||
---|---|---|
| ||
int limit = (1 << 17) - 1; // 2^17 - 1 = 131071 |
Nevertheless, as a matter of style, it is preferable to replace such constant expressions with the equivalent hexadecimal constants.
Code Block | ||
---|---|---|
| ||
int limit = 0x1FFFF; // 2^17 - 1 = 131071
|
NUM01-EX1: Data that is normally treated arithmetically may be treated with bitwise operations for the purpose of serialization or deserialization. This is often required for reading or writing the data from a file or network socket. Bitwise operations are also permitted when reading or writing the data from a tightly packed data structure of bytes.
Code Block | ||
---|---|---|
| ||
int value = /* interesting value */
Byte[] bytes = new Byte[4];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = value >> (i*8) & 0xFF;
}
/* bytes[] now has same bit representation as value */
|
...
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
NUM01-J | medium | unlikely | medium | P4 | L3 |
Related Guidelines
INT14-C. Avoid performing bitwise and arithmetic operations on the same data | |
INT14-CPP. Avoid performing bitwise and arithmetic operations on the same data |
Bibliography
03. Numeric Types and Operations (NUM) NUM02-J. Ensure that division and modulo operations do not result in divide-by-zero errors