543

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;
}
Hash
  • 4,647
  • 5
  • 21
  • 39
Brigham
  • 14,395
  • 3
  • 38
  • 48
  • 11
    I'm curious as to why you would have to do this. If you have a `long`, why can't you just work with a `long`? That way, you'll never have to worry about this. – Thomas Owens Oct 19 '09 at 20:12
  • 36
    Two code paths. One is legacy and needs ints. That legacy data SHOULD all fit in an int, but I want to throw an exception if that assumption is violated. The other code path will use longs and won't need the cast. – Brigham Oct 19 '09 at 20:21
  • 15
    There are lots of things you can't do with longs eg index an array. – skot Feb 10 '13 at 23:05
  • 8
    @TommyHerbert, How on earth is safe converting of longs to ints a code smell? – Pacerier Sep 01 '14 at 08:02
  • 1
    @Pacerier: In general, you shouldn't have a long in the first place if what you need is an int, -slash- you shouldn't be downcasting to int if what you needed was a long. (Remember, "code smell" doesn't mean "bad code", it means "code that suggests that there might be a problem".) – ruakh Oct 04 '16 at 04:41
  • Generally avoid assuming how a broken cast would work. Certainly C compilers can do weird things. Better to test against max int and min int, or to mask off the bottom 32 bits. Not checking for integer overflow is an idiocy Java inherited from C. It costs nothing using hardware traps. More sophisticated languages like Visual Basic do check by default, and that has saved me on a few occasions. – Tuntable Jul 31 '20 at 09:04

10 Answers10

647

A method was added in Java 8:

import static java.lang.Math.toIntExact;

long foo = 10L;
int bar = toIntExact(foo);

Will throw an ArithmeticException in case of overflow.

See: Math.toIntExact(long)

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)
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Pierre-Antoine
  • 7,939
  • 6
  • 28
  • 36
  • 7
    We also have `addExact` and `multiplyExact`. Of note is that division (`MIN_VALUE/-1`) and absolute value (`abs(MIN_VALUE)`) do not have safe convenience methods. – Aleksandr Dubinsky Oct 27 '17 at 09:43
  • But what's a difference of using `Math.toIntExact()` instead of usual cast to `int`? The implementation of `Math.toIntExact()` just casts `long` to `int`. – Yamashiro Rion May 03 '19 at 10:15
  • 3
    @YamashiroRion Actually the implementation of toIntExact first checks whether the cast would lead to an overflow, in which case it throws an ArithmeticException. Only if the cast is safe it then performs cast from long to int which it returns. In other words, if you try to cast a long number that cannot be represented as int (eg any number strictly above 2 147 483 647) it will throw an ArithmeticException. If you do the same with a simple cast, your resulting int value will be wrong. – Pierre-Antoine May 03 '19 at 12:15
304

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);
James McMahon
  • 48,506
  • 64
  • 207
  • 283
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 7
    I'd always do the range check as `(!(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`. – Tom Hawtin - tackline Oct 19 '09 at 20:24
  • 5
    +1. This falls exactly under the "exceptions should be used for *exceptional* conditions" rule. – Adam Rosenfield Oct 19 '09 at 20:24
  • 4
    (In a modern general-purpose language it would be: "Eh? But ints have arbitrary size?") – Tom Hawtin - tackline Oct 19 '09 at 20:27
  • 7
    @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 Oct 19 '09 at 20:56
  • 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 Oct 20 '09 at 01:38
  • 6
    @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 Oct 20 '09 at 05:20
  • I suggest using ArithmeticException instead, to match the interface of the Java8 Math.toIntExact(...). – Gladclef Apr 12 '17 at 17:45
136

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.

Henrik Aasted Sørensen
  • 6,966
  • 11
  • 51
  • 60
brabec
  • 4,632
  • 8
  • 37
  • 63
  • 3
    Guava's `Ints.checkedCast` does exactly what OP does, incidentally – Partly Cloudy Feb 16 '13 at 00:31
  • 15
    +1 for Guava solution, though no real need to wrap it in another method, just call `Ints.checkedCast(l)` directly. – dimo414 Oct 07 '13 at 19:46
  • 8
    Guava also has `Ints.saturatedCast` which will return the nearest value instead of throwing an exception. – Jake Walsh Jun 30 '14 at 17:38
  • Yes, it's safe to use existing api as my case, the library already in the project: to throw exception if invalid: Ints.checkedCast(long) and Ints.saturatedCast(long) to get the nearest for converting long to int. – Osify Jun 17 '15 at 03:55
29

With BigDecimal:

long aLong = ...;
int anInt = new BigDecimal(aLong).intValueExact(); // throws ArithmeticException
                                                   // if outside bounds
Jaime Saiz
  • 491
  • 6
  • 5
  • 13
    Well, it's allocating and throwing away a BigDecimal just to get at what should be a utility method, so yeah, that's not the best process. – Riking Apr 23 '14 at 20:28
  • @Riking in that regard, it’s better to use `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. – Holger Nov 07 '18 at 14:08
17

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);
}
Vitaliy Kulikov
  • 288
  • 2
  • 5
11

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

  1. Long.valueOf creates a new Long instance if the number is not within Long's pool range [-128, 127].

  2. The intValue implementation does nothing more than:

    return (int)value;
    

So this can be considered even worse than just casting the long to int.

Andreas
  • 3,929
  • 2
  • 25
  • 22
  • 4
    Your attempt to help is appreciated, but giving an example of something that doesn't work isn't quite the same as providing a solution that does work. If you would edit to add a correct way, this could be pretty good; otherwise, it's not really suitable to be posted as an answer. – Pops Sep 19 '13 at 00:52
  • 4
    Okay, why not having both DOs and DONTs? Tbh, sometimes I wish I had a list of how to not do things (DONTs) to check whether I used such a pattern/code. Anyway, I can delete this "answer". – Andreas Sep 19 '13 at 10:50
  • 1
    Good anti-pattern. Anyway it would have been great if you explained what happens if the long value is out-of-range for int? I guess there will be a ClassCastException or something like this? – Peter Wippermann Oct 02 '13 at 06:27
  • 2
    @PeterWippermann: I have added some more info. Do you consider them understandable resp. explanatory enough? – Andreas Oct 02 '13 at 11:22
7

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
  • 145,806
  • 30
  • 211
  • 305
2

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("...");
    }
}
mob
  • 117,087
  • 18
  • 149
  • 283
  • 3
    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 Oct 19 '09 at 20:24
  • @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. – mob Oct 19 '09 at 20:47
  • 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 Oct 19 '09 at 20:55
  • 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. – mob Oct 19 '09 at 21:22
  • @mob what is this in reference to? OP's code correctly reports that long values > 2^{31} cannot be casted to ints – Partly Cloudy Feb 16 '13 at 00:33
  • In Java, any primitive type can be cast to int. – mob Feb 16 '13 at 00:42
0
(int) (longType + 0)

but Long can not exceed the maximum :)

Maury
  • 604
  • 2
  • 8
  • 25
  • 1
    The `+ 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. – sixones Sep 30 '16 at 13:41
-7

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.

Rajat Anantharam
  • 495
  • 1
  • 3
  • 17
  • You will get a NumberFormatException on "real long" values: `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. – Andreas Oct 10 '13 at 13:33
  • 2
    It also doesn't compile because you don't always return a value. – Patrick M Apr 01 '15 at 17:26