Conversions of numeric types to narrower types can result in lost or misinterpreted data if the value of the wider type is outside the range of values of the narrower type. As a resultConsequently, all narrowing conversions must be guaranteed safe by range-checking the value before conversion.
There are Java provides 22 possible narrowing primitive conversions in Java. According to the The Java Languagee Specification (JLS), §5.1.3, "Narrowing Primitive Conversions" [JLS 20052015]:
short
tobyte
orchar
char
tobyte
orshort
int
tobyte
,short
, orchar
long
tobyte
,short
,char
, orint
float
tobyte
,short
,char
,int
, orlong
double
tobyte
,short
,char
,int
,long
, orfloat
...
Integer type ranges are defined by the JLS, §4.2.1, "Integral Types and Values" [JLS 20052015], and are also described in rule NUM00-J. Detect or prevent integer overflow.
The following table presents the rules for narrowing primitive conversions of integer types. In the table, for an integer type T
, n
represents the number of bits used to represent the resulting type T
(precision).
From | To | Description | Possible Resulting Errors |
---|
Signed integer |
Integral type | Keeps only | Lost or misinterpreted data |
|
Integral type | Keeps only | Magnitude error; negative number even though |
When integers are cast to narrower data types, the magnitude of the numeric value and the corresponding sign can be affected. Consequently, data can be lost or misinterpreted.
...
1. When converting a floating-point value to an int
or long
and the value is a NaN
, a zero value is produced. Otherwise, if the value is not infinity, it is rounded towards toward zero to an integer value V
:
...
- The value is negative infinity or a value too negative to be represented, and
Integer.MIN_VALUE
orLong.MIN_VALUE
is produced. - The value is positive infinity or a value too positive to be represented, and
Integer.MAX_VALUE
orLong.MAX_VALUE
is produced.
...
See the JLS, §5.1.3, "Narrowing Primitive Conversions," [JLS 2005], for more information.
Other Conversions
Narrower primitive types can be cast to wider types without affecting the magnitude of numeric values . See (see the JLS, §5.1.2, Widening Primitive Conversion" [JLS 2005]), for more information). Conversion from int
or long
to float
or from long
to double
can lead to loss of precision (loss of least significant bits). No runtime exception occurs despite this loss.
Note that conversions from float
to double
or from double
to float
can also lose information about the overall magnitude of the converted value . See rule NUM06(see NUM53-J. Use the strictfp modifier for floating-point calculation consistency across platforms for additional information).
Noncompliant Code Example (Integer Narrowing)
In this noncompliant code example, a value of type int
is converted to a value of type byte
without range checking.:
Code Block | ||
---|---|---|
| ||
class CastAway { public static void main(String[] args) { int i = 128; workWith(i); } public static void workWith(int i) { byte b = (byte) i; // b has value -128 // workWork with b } } |
The resulting value may be unexpected because the initial value (128) is outside of the range of the resulting type.
...
This compliant solution validates that the value stored in the wider integer type is within the range of the narrower type before converting to the narrower type. It throws an ArithmeticException
if the value is out of range.
Code Block | ||
---|---|---|
| ||
class CastAway { public static void workWith(int i) throws ArithmeticException { // checkCheck ifwhether i is within byte range if ((i < Byte.MIN_VALUE) || (i > Byte.MAX_VALUE)) { throw new ArithmeticException("Value is out of range"); } byte b = (byte) i; // workWork with b } } |
Alternatively, the workWith()
method can explicitly narrow when the programmer's intent is to truncate the value:
Code Block | ||
---|---|---|
| ||
class CastAway { public static void workWith(int i) { byte b = (byte)(i % 0x100); // 2^8; // Work with b } } |
Range-checking is unnecessary because the truncation that is normally implicit in a narrowing conversion is made explicit. The compiler will optimize the operation away, and for that reason, no performance penalty is incurred. Similar operations may be used for converting to other integral types.
Noncompliant Code Example (Floating-Point
...
to
...
Integer Conversion)
The narrowing primitive conversions in this noncompliant code example suffer from loss in the magnitude of the numeric value as well as a loss of precision.:
Code Block | ||
---|---|---|
| ||
float i = Float.MIN_VALUE; float j = Float.MAX_VALUE; short b = (short) i; short c = (short) j; |
The minimum and maximum float
values are converted to minimum 0 and maximum int
values (0x80000000
and 0x7fffffff
respectively). The resulting short
values are 0 and the lower 16 bits of these values this value (0x0000
and 0xffff
). The resulting final values (0 and -1−1) might be unexpected.
Compliant Solution (Floating-Point to Integer Conversion)
This compliant solution range-checks both the i
and j
variables before converting to the resulting integer type. Because both values are the maximum value is out of the valid range for a short
, this code will always throw an ArithmeticException
.
Code Block | ||
---|---|---|
| ||
float i = Float.MIN_VALUE; float j = Float.MAX_VALUE; if ((i < Short.MIN_VALUE) || (i > Short.MAX_VALUE) || (j < Short.MIN_VALUE) || (j > Short.MAX_VALUE)) { throw new ArithmeticException ("Value is out of range"); } short b = (short) i; short c = (short) j; // otherOther operations |
Noncompliant Code Example (double
to float
Conversion)
The narrowing primitive conversions in this noncompliant code example suffer from a loss in the magnitude of the numeric value as well as a loss of precision. Because Double.MAX_VALUE
is larger than Float.MAX_VALUE
, c
receives the value infinity
, and because Double.MIN_VALUE
is smaller than Float.MIN_VALUE
, b
receives the value 0
.
...
Code Block | ||
---|---|---|
| ||
double i = Double.MIN_VALUE;
double j = Double.MAX_VALUE;
if ((i < Float.MIN_VALUE) || (i > Float.MAX_VALUE) ||
(j < Float.MIN_VALUE) || (j > Float.MAX_VALUE)) {
throw new ArithmeticException ("Value is out of range");
}
float b = (float) i;
float c = (float) j;
// other operations
|
Exceptions
NUM12-EX0: Java's narrowing conversions are both well defined and portable. The effects of narrowing on integral types can be easily reproduced in code; however, the effects of narrowing on floating-point types and between floating-point types and integral types cannot be easily represented. Knowledgeable programmers may intentionally apply narrowing conversions involving floating-point types in contexts where their output is both expected and reasonable. Consequently, narrowing conversions are permitted when the code contains comments that document both the use of narrowing conversions and the anticipated truncation. A suitable comment might read:
// Deliberate narrowing cast of i; possible truncation OK
This exception does not permit narrowing conversions without range-checking among integral types. The following code example demonstrates how to perform explicit narrowing from a long
to an int
where range-checking is not required.
Code Block | ||
---|---|---|
| ||
long value = /* initialize */;
int i = (int) (value % 0x100000000L); // 2^32
|
The range-checking is unnecessary because the truncation that is normally implicit in a narrowing conversion is made explicit. The compiler will optimize the operation away, and for that reason, no performance penalty is incurred.
Similar operations may be used for converting to other integral types.
Other operations
|
Risk Assessment
Casting a numeric value to a narrower type can result in information loss related to the sign and magnitude of the numeric value. As a result, data can be misrepresented or interpreted incorrectly.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
NUM12-J |
Low |
Unlikely |
Medium | P2 | L3 |
Automated Detection
Automated detection of narrowing conversions on integral types is straightforward. Determining whether such conversions correctly reflect the intent of the programmer is infeasible in the general case. Heuristic warnings could be useful.
Tool | Version | Checker | Description | |||||
---|---|---|---|---|---|---|---|---|
CodeSonar |
| JAVA.MATH.APPROX.E |
...
FPEQUAL | Approximate e Constant (Java) | ||||||||
Parasoft Jtest |
| CERT.NUM12.CLP | Do not cast primitive data types to lower precision |
Related Guidelines
Related Guidelines
INT31-C. Ensure that integer conversions do not result in lost or misinterpreted data
FLP34-C. Ensure that floating-point conversions are within range of the new type
C. Ensure that integer conversions do not result in lost or misinterpreted data |
C. Ensure that floating-point conversions are within range of the new type | |
Numeric Conversion Errors [FLC] | |
, Incorrect |
Conversion between Numeric Types |
, Numeric |
Truncation Error |
Bibliography
[JLS 2005] | §4.2.1, "Integral Types and Values" |
[Seacord 2015] |
...