tags:

views:

6241

answers:

3

What's the most idiomatic way in Java to verify that a cast from long to int did 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;
}
+14  A: 

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);
Jon Skeet
Aren't your > and < reversed?
Thomas Owens
No, my values were ;) (I was already editing...)
Jon Skeet
Thanks. This does express my intent more clearly.
Brigham
Tom Hawtin - tackline
+1. This falls exactly under the "exceptions should be used for *exceptional* conditions" rule.
Adam Rosenfield
Now I can upvote. I like it.
Thomas Owens
(In a modern general-purpose language it would be: "Eh? But ints have arbitrary size?")
Tom Hawtin - tackline
@Tom: Personal preference, I guess - I prefer to have as few negatives as possible. If I'm looking at an "if" with a body which throws an exception, I'd like to see conditions that make it look exceptional - like the value being "off the bottom end" of `int`.
Jon Skeet
I look at it from a whitelist sort of view point. These are the values that *I am* interested in. The language design forces the negative.
Tom Hawtin - tackline
@Tom: In that case I'd remove the negative, put the cast/return inside the "if" body, and then throw an exception afterwards, if you see what I mean.
Jon Skeet
A: 

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("...");
    }
}
mobrule
I don't see what signedness has to do with it. Could you give an example which *doesn't* lose information but *does* fail the test? 2^31 would be cast to Integer.MIN_VALUE (i.e. -2^31) so information has been lost.
Jon Skeet
@Jon Skeet: Maybe me and the OP are talking past each other. `(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.
mobrule
How can you extract the original long value from the int, when the long could have been -1 to start with, instead of 0xFFFFFFFF?
Jon Skeet
Sorry if I"m not clear. I'm saying that if the long and the int both contain the same 32 bits of information, and if the 32nd bit is set, then the int value is different from the long value, but that it is easy to get the long value.
mobrule
+2  A: 

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.

Tom Hawtin - tackline