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
Java: How do I perform integer division that rounds towards -Infinity rather than 0?
提问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 int
and long
arithmetic.)
(更新:我想有一个解决方案int
和long
算术。)
回答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 < 0
and d > 0
: take the bitwise complement of n, do the division, and then take the bitwise complement of the result.
有一个相当简洁的公式,适用于n < 0
和d > 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 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)
.
对于其余部分,类似的构造工作(ifloordiv
在ifloordiv(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 ifloordiv
and ifloormod
that 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 == -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.
对于d < 0
,当d == -1
和n
是时有一个不可避免的问题Integer.MIN_VALUE
,因为数学结果会溢出类型。在这种情况下,上面的公式返回包装的结果,就像通常的 Java 除法一样。据我所知,这是我们默默地得到“错误”结果的唯一极端情况。
回答by trutheality
(I'm doing everything for long
s since the answer for int
s is the same, just substitute int
for every long
and Integer
for every Long
.)
(我正在为long
s做所有事情,因为s 的答案int
是相同的,只是替换int
每个long
和Integer
每个Long
。)
You could just Math.floor
a 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 BigDecimal
with a ROUND_FLOOR
rounding 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();