What's the most idiomatic way in Java to verify that a cast from long
to int
does not lose any information?
This is my current implementation:
public static int safeLongToInt(long l) {
int i = (int)l;
if ((long)i != l) {
throw new IllegalArgumentException(l + " cannot be cast to int without changing its value.");
}
return i;
}
A new method has been added with Java 8 to do just that.
import static java.lang.Math.toIntExact;
long foo = 10L;
int bar = toIntExact(foo);
Will throw an ArithmeticException
in case of overflow.
Several other overflow safe methods have been added to Java 8. They end with exact.
Examples:
Math.incrementExact(long)
Math.subtractExact(long, long)
Math.decrementExact(long)
Math.negateExact(long),
Math.subtractExact(int, int)
I think I'd do it as simply as:
public static int safeLongToInt(long l) {
if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
throw new IllegalArgumentException
(l + " cannot be cast to int without changing its value.");
}
return (int) l;
}
I think that expresses the intent more clearly than the repeated casting... but it's somewhat subjective.
Note of potential interest - in C# it would just be:
return checked ((int) l);
(!(Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE))
. I find it difficult to get my head around other ways of doing it. Pity Java does not have unless
.
int
.
With Google Guava's Ints class, your method can be changed to:
public static int safeLongToInt(long l) {
return Ints.checkedCast(l);
}
From the linked docs:
checkedCast public static int checkedCast(long value) Returns the int value that is equal to value, if possible. Parameters: value - any value in the range of the int type Returns: the int value that equals value Throws: IllegalArgumentException - if value is greater than Integer.MAX_VALUE or less than Integer.MIN_VALUE
Incidentally, you don't need the safeLongToInt
wrapper, unless you want to leave it in place for changing out the functionality without extensive refactoring of course.
Ints.checkedCast
does exactly what OP does, incidentally
Ints.checkedCast(l)
directly.
Ints.saturatedCast
which will return the nearest value instead of throwing an exception.
With BigDecimal:
long aLong = ...;
int anInt = new BigDecimal(aLong).intValueExact(); // throws ArithmeticException
// if outside bounds
BigDecimal.valueOf(aLong)
, instead of new BigDecimal(aLong)
, to denote that a new instance is not required. Whether the execution environment does caching at that method, is implementation specific, just like the possible presence of Escape Analysis. In most real life cases, this has no impact on the performance.
here is a solution, in case you don't care about value in case it is bigger then needed ;)
public static int safeLongToInt(long l) {
return (int) Math.max(Math.min(Integer.MAX_VALUE, l), Integer.MIN_VALUE);
}
too low
? please, provide use-case.
DONT: This is not a solution!
My first approach was:
public int longToInt(long theLongOne) {
return Long.valueOf(theLongOne).intValue();
}
But that merely just casts the long to an int, potentially creating new Long
instances or retrieving them from the Long pool.
The drawbacks
Long.valueOf creates a new Long instance if the number is not within Long's pool range [-128, 127]. The intValue implementation does nothing more than: return (int)value;
So this can be considered even worse than just casting the long
to int
.
I claim that the obvious way to see whether casting a value changed the value would be to cast and check the result. I would, however, remove the unnecessary cast when comparing. I'm also not too keen on one letter variable names (exception x
and y
, but not when they mean row and column (sometimes respectively)).
public static int intValue(long value) {
int valueInt = (int)value;
if (valueInt != value) {
throw new IllegalArgumentException(
"The long value "+value+" is not within range of the int type"
);
}
return valueInt;
}
However, really I would want to avoid this conversion if at all possible. Obviously sometimes it's not possible, but in those cases IllegalArgumentException
is almost certainly the wrong exception to be throwing as far as client code is concerned.
Java integer types are represented as signed. With an input between 231 and 232 (or -231 and -232) the cast would succeed but your test would fail.
What to check for is whether all of the high bits of the long
are all the same:
public static final long LONG_HIGH_BITS = 0xFFFFFFFF80000000L;
public static int safeLongToInt(long l) {
if ((l & LONG_HIGH_BITS) == 0 || (l & LONG_HIGH_BITS) == LONG_HIGH_BITS) {
return (int) l;
} else {
throw new IllegalArgumentException("...");
}
}
(int) 0xFFFFFFFF
and (long) 0xFFFFFFFFL
have different values, but they both contain the same "information", and it is almost trivial to extract the original long value from the int.
(int) (longType + 0)
but Long can not exceed the maximum :)
+ 0
adds nothing to this conversion, it might work if Java treated numeric type concatenation in a similar way to strings, but since it doesn't your doing an add operation for no reason.
One other solution can be:
public int longToInt(Long longVariable)
{
try {
return Integer.valueOf(longVariable.toString());
} catch(IllegalArgumentException e) {
Log.e(e.printstackstrace());
}
}
I have tried this for cases where the client is doing a POST and the server DB understands only Integers while the client has a Long.
Integer.valueOf(Long.MAX_VALUE.toString());
results in java.lang.NumberFormatException: For input string: "9223372036854775807"
that pretty much obfuscates the Out-Of-Range-Exception because it is now treated in the same way a string containing letters is treated.
Success story sharing
addExact
andmultiplyExact
. Of note is that division (MIN_VALUE/-1
) and absolute value (abs(MIN_VALUE)
) do not have safe convenience methods.Math.toIntExact()
instead of usual cast toint
? The implementation ofMath.toIntExact()
just castslong
toint
.