Java:如何执行向 -Infinity 而不是 0 舍入的整数除法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10457208/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 01:07:20  来源:igfitidea点击:

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

javamathintegerdivision

提问by Jason S

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

注意:与另一个问题不同,因为 OP 从未明确指定向 0 或 -Infinity 取整)

JLS 15.17.2says that integer division rounds towards zero. If I want floor()-like behavior for positive divisors (I don't care about the behavior for negative divisors), what's the simplest way to achieve this that is numerically correct for all inputs?

JLS 15.17.2说整数除法向零舍入。如果我想要floor()正除数的类似行为(我不关心负除数的行为),那么实现这一点的最简单方法是什么,对于所有输入在数值上都是正确的?

int ifloor(int n, int d)
{
    /* returns q such that n = d*q + r where 0 <= r < d
     * for all integer n, d where d > 0
     *
     * d = 0 should have the same behavior as `n/d`
     *
     * nice-to-have behaviors for d < 0:
     *   option (a). same as above: 
     *     returns q such that n = d*q + r where 0 <= r < -d
     *   option (b). rounds towards +infinity:
     *     returns q such that n = d*q + r where d < r <= 0
     */
}

long lfloor(long n, long d)
{
    /* same behavior as ifloor, except for long integers */
}

(update: I want to have a solution both for intand longarithmetic.)

(更新:我想有一个解决方案intlong算术。)

回答by Louis Wasserman

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

如果你可以使用第三方库,Guava有这个:IntMath.divide(int, int, RoundingMode.FLOOR)LongMath.divide(int, int, RoundingMode.FLOOR). (披露:我为番石榴做出了贡献。)

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

如果您不想为此使用第三方库,您仍然可以查看实现。

回答by Mark Dickinson

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

有一个相当简洁的公式,适用于n < 0d > 0:取 n 的按位补码,进行除法,然后取结果的按位补码。

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 ifloordivin the sense that the usual invariant ifloordiv(n, d) * d + ifloormod(n, d) == nis satisfied) giving a result that's always in the range [0, d).

对于其余部分,类似的构造工作(ifloordivifloordiv(n, d) * d + ifloormod(n, d) == n满足通常不变量的意义上兼容)给出始终在范围内的结果[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 ifloordivand ifloormodthat follow your 'nice-to-have' behavior option (b) for negative divisors.

对于负除数,公式并不那么简洁。这里有扩大的版本ifloordiv,并ifloormod遵循您的“最好到具有”负除数行为选项(B)。

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 == -1and nis 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.

对于d < 0,当d == -1n是时有一个不可避免的问题Integer.MIN_VALUE,因为数学结果会溢出类型。在这种情况下,上面的公式返回包装的结果,就像通常的 Java 除法一样。据我所知,这是我们默默地得到“错误”结果的唯一极端情况。

回答by trutheality

(I'm doing everything for longs since the answer for ints is the same, just substitute intfor every longand Integerfor every Long.)

(我正在为longs做所有事情,因为s 的答案int是相同的,只是替换int每个longInteger每个Long。)

You could just Math.floora double division result, otherwise...

你可能只是Math.floor一个双除的结果,否则......

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 BigDecimalwith a ROUND_FLOORrounding mode is also an option.)

(为了完整起见,使用BigDecimal带有ROUND_FLOOR舍入模式的a也是一种选择。)

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

新编辑:现在我只是想看看它可以优化到什么程度以获得乐趣。到目前为止,我最好的使用Mark 的答案是:

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)).

(使用比上述更便宜的操作,但字节码稍长(29 对 26))。

回答by jtahlborn

return BigDecimal.valueOf(n).divide(BigDecimal.valueOf(d), RoundingMode.FLOOR).longValue();