Java: How do I perform integer division that rounds towards -Infinity rather than 0?

前端 未结 4 1525
轮回少年
轮回少年 2020-12-17 16:20

(note: not the same as this other question since the OP never explicitly specified rounding towards 0 or -Infinity)

JLS 15.17.2 says that integer di

相关标签:
4条回答
  • 2020-12-17 16:55

    (I'm doing everything for longs since the answer for ints is the same, just substitute int for every long and Integer for every Long.)

    You could just Math.floor a double division result, otherwise...

    Original answer:

    return n/d - ( ( n % d != 0 ) && ( (n<0) ^ (d<0) ) ? 1 : 0 );
    

    Optimized answer:

    public static long lfloordiv( long n, long d ) {
    
        long q = n/d;
        if( q*d == n ) return q;
        return q - ((n^d) >>> (Long.SIZE-1));
    }
    

    (For completeness, using a BigDecimal with a ROUND_FLOOR rounding mode is also an option.)

    New edit: Now I'm just trying to see how far it can be optimized for fun. Using Mark's answer the best I have so far is:

    public static long lfloordiv2( long n, long d ){
    
        if( d >= 0 ){
            n = -n;
            d = -d;
        }
        long tweak = (n >>> (Long.SIZE-1) ) - 1;
        return (n + tweak) / d + tweak;
    }
    

    (Uses cheaper operations than the above, but slightly longer bytecode (29 vs. 26)).

    0 讨论(0)
  • 2020-12-17 17:07

    There's a rather neat formula for this that works when n < 0 and d > 0: take the bitwise complement of n, do the division, and then take the bitwise complement of the result.

    int ifloordiv(int n, int d)
    {
        if (n >= 0)
            return n / d;
        else
            return ~(~n / d);
    }
    

    For the remainder, a similar construction works (compatible with ifloordiv in the sense that the usual invariant ifloordiv(n, d) * d + ifloormod(n, d) == n is satisfied) giving a result that's always in the range [0, d).

    int ifloormod(int n, int d)
    {
        if (n >= 0)
            return n % d;
        else
            return d + ~(~n % d);
    }
    

    For negative divisors, the formulas aren't quite so neat. Here are expanded versions of ifloordiv and ifloormod that follow your 'nice-to-have' behavior option (b) for negative divisors.

    int ifloordiv(int n, int d)
    {
        if (d >= 0)
            return n >= 0 ? n / d : ~(~n / d);
        else
            return n <= 0 ? n / d : (n - 1) / d - 1;
    }
    
    int ifloormod(int n, int d)
    {
        if (d >= 0)
            return n >= 0 ? n % d : d + ~(~n % d);
        else
            return n <= 0 ? n % d : d + 1 + (n - 1) % d;
    }
    

    For d < 0, there's an unavoidable problem case when d == -1 and n is Integer.MIN_VALUE, since then the mathematical result overflows the type. In that case, the formula above returns the wrapped result, just as the usual Java division does. As far as I'm aware, this is the only corner case where we silently get 'wrong' results.

    0 讨论(0)
  • 2020-12-17 17:12

    If you can use third-party libraries, Guava has this: IntMath.divide(int, int, RoundingMode.FLOOR) and LongMath.divide(int, int, RoundingMode.FLOOR). (Disclosure: I contribute to Guava.)

    If you don't want to use a third-party library for this, you can still look at the implementation.

    0 讨论(0)
  • 2020-12-17 17:17
    return BigDecimal.valueOf(n).divide(BigDecimal.valueOf(d), RoundingMode.FLOOR).longValue();
    
    0 讨论(0)
提交回复
热议问题