2012-05-04 2 views
12

ノート:OP以来this other questionと同じではない、明示的に0または-Infinityに向けて丸め、指定されません)Java:0よりむしろ-Infinityに丸められる整数除算を実行するにはどうすればよいですか?

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 */ 
} 

(更新:私は両方intlong算術演算のためのソリューションを持っていると思います)

+0

この*重複して*いるが、私はそれを見つけることができない、それが重複しないならば、私はそれはStackOverflowのの3年以上後にまだ来ていないだけで、完全に驚いています。 –

+0

残りの 'd <0 'の条件では、あなたは2つの反転した兆候があると思います。オプション(a)では '0 <= r <-d'、オプション(b)では' d

+0

権利:ありがとう、私は除数の正のバージョンを意味した。 (それは+無限に向かって丸めていたはずです) –

答えて

6

intのための答えは同じであるので、(私はlongのためにすべてをやっている、ちょうどintを代用すべてのLongごとlongIntegerため。)

あなたは可能性だけMath.floorダブル除算結果、そうでなければ...

オリジナルの答え:

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

最適化された答え:

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)); 
} 

(完全を期すため、ROUND_FLOOR丸めモードでBigDecimalを使用することもオプションです。)

新編集:今私はちょうどそれが楽しいために最適化することができます参照しようとしています。 Mark's answerを使用して、私がこれまで持っている最高のは、次のとおりです。

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; 
} 

(上記よりも安いの操作を使用していますが、やや長いバイトコード(29対26))。

+0

Math.floorを参照しないでください。 'int'変数(これは私がはっきりしていない)に対して正しく動作する場合、精度の欠如のために' long'変数にはうまくいかないでしょう。 –

+0

私は1分後にドアを出ていますが、月曜日に正しいかどうかチェックすることができれば+1します。ありがとう+良い週末を持って.... –

+2

ニース!簡単な難読化最適化: '(n <0)^(d <0)'を 'n^d <0'に置き換えます。コンパイラはこの最適化を_might_しますが、私はそれを疑っています。 –

7

サードパーティのライブラリを使用できる場合、Guavaには、IntMath.divide(int, int, RoundingMode.FLOOR)LongMath.divide(int, int, RoundingMode.FLOOR)があります。 (開示:私はGuavaに貢献します)

サードパーティのライブラリを使用したくない場合でも、実装を見ることができます。

+0

グアバは非常に評判の良いライブラリです。 –

+0

Guavaにdouble型の除算関数が含まれていないのはなぜですか? – voipp

+0

@voipp:あなたはそれで何をしますか?それは通常の二重除算とどのように違うでしょうか? –

6

n < 0d > 0:nのビット補数をとり、除算を行い、結果のビット補数を取るときには、これはちょっときちんとした式があります。残りについて

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

、範囲[0, d)常にだ結果を与える(通常不変ifloordiv(n, d) * d + ifloormod(n, d) == nが満たされているという意味でifloordiv対応)同様の工事。

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

負の除数の場合、数式はきれいではありません。負の約数のためのあなたの「いいもの」の動作オプション(b)に続くifloordivifloormodの拡張バージョンがあります。d < 0については

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; 
} 

、その後、数学的な結果はタイプをオーバーフローするのでd == -1nは、Integer.MIN_VALUEで避けられない問題の場合があります。その場合、上記の式は通常のJava部門と同様にラップされた結果を返します。私が知っている限り、これは黙って「間違った」結果を得る唯一のコーナーケースです。

+0

@trutheality:ガー、私はばかです!ありがとう。すぐにこれを修正します! –

+0

@trutheality:結果がまだMIN_VALUEとして出てくることを除いて、真ですが、数学的には間違っています(ただし、2 **​​のモジュロ2で正しいので、十分かもしれません)。私は 'd <0'のために他のコーナーケースが潜んでいるという厄介な気持ちを持っていますが、彼らが何であるかを理解する時間はかかりませんでした。多分私はそれをするべきです。 –

+0

ええ、私は分裂が実際にあふれていることに気づいていませんでした。 – trutheality

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