Javaの係数を負の数の場合と同じように動作させる最良の方法は?


103

あなたがするときにJavaで

a % b

aが負の場合、本来のようにbにラップする代わりに、負の結果を返します。これを修正する最良の方法は何ですか?私が考えることができる唯一の方法は

a < 0 ? b + a : a % b

12
負の数を処理するときの「正しい」モジュラス動作はありません。多くの言語がこのようにして、多くの言語が異なる方法で実行し、一部の言語はまったく異なる処理を実行します。少なくとも最初の2つには長所と短所があります。

4
これは私にとって奇妙なことです。私はそれがbが負の場合にのみ負を返すべきだと思った。
fent


2
そうです。しかし、その質問のタイトルは名前を変更する必要があります。Javaモジュラスがどのように機能するかはすでに知っているので、この質問を探していても、その質問をクリックしません。
fent

4
「なぜ-13%64 = 51なのか」という名前に変更しましたが、100万年後には検索されるものにはなりません。したがって、この質問のタイトルははるかに優れており、係数、負の値、計算、数値などのキーワードではるかに検索可能です。
エリックロバートソン

回答:


144

本来あるべき動作をしますa%b = a-a / b * b; つまり、残りです。

あなたはできる(a%b + b)%b


この式(a % b)baが正であるか負であるかに関係なく、の結果が必然的によりも低い場合に機能します。追加bの負の値の世話をするaので、(a % b)間の負の値である-b0(a % b + b)必ずしも未満であるbと肯定。最後のモジュロは場合にあっているaからあれば、そもそも陽性であったa肯定的である(a % b + b)よりも大きくなりますb。したがって、(a % b + b) % bそれをb再びより小さくします(負のa値には影響しません)。


3
これはよりよく機能します。また、bよりもはるかに大きい負の数でも機能します。
fent

6
これは、結果ので動作(a % b)必ずしもより低いb(場合に関係なくa追加すること、正または負である)bの負の値の世話をaするので、(a % b)より低いb及びより低い0(a % b + b)必ずしも未満であるbと肯定。最後のモジュロは場合にあっているaからあれば、そもそも陽性であったa肯定的である(a % b + b)よりも大きくなりますb。したがって、(a % b + b) % bそれをb再びより小さくします(負のa値には影響しません)。
ethanfar 14

1
@eitanfar私はあなたの優れた説明を答えに含めました(のためのマイナーな修正をしてa < 0、多分あなたは見ることができるかもしれません)
Maarten Bodewes

5
私はこれが同じトピックに関する別の質問にコメントしたのを見ました。との(a % b + b) % b非常に大きな値に対してはa、が機能しなくなることに言及する価値がありbます。たとえば、a = Integer.MAX_VALUE - 1and b = Integer.MAX_VALUEを使用すると-3、結果として負の数が得られますが、これは避けたいものです。
Thorbear

2
@Mikepoteを使用するwhileと、必要な場合を除いて、本当に必要な場合は遅くなりますif
Peter Lawrey 2016年

92

Java 8以降では、Math.floorMod(int x、int y)Math.floorMod(long x、long y)を使用できます。これらのメソッドはどちらも、Peterの回答と同じ結果を返します。

Math.floorMod( 2,  3) =  2
Math.floorMod(-2,  3) =  1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2

1
Java 8+のベストアンサー
Charney Kaye

あれは知らなかった。Java 8はいくつかのPITAを確実に修正しました。
フランツD.

4
良い方法。しかし、残念ながらfloatdouble引数では機能しません。Mod二項演算子%)もfloatand doubleオペランドで機能します。
Mir-Ismaili

11

Java 8をまだ使用していない(または使用できない)人々のために、Guavaは、Guava 11.0以降で利用可能なIntMath.mod()を使用して助けを求めました

IntMath.mod( 2, 3) = 2
IntMath.mod(-2, 3) = 1

注意点の1つ:Java 8のMath.floorMod()とは異なり、除数(2番目のパラメーター)を負にすることはできません。


7

数論では、結果は常に正です。すべてのプログラマーが数学者であるとは限らないため、コンピューター言語ではこれが常に当てはまるとは限らないと思います。私の2セントは、言語の設計上の欠陥だと思いますが、今は変更できません。

= MOD(-4,180)= 176 = MOD(176、180)= 176

180 *(-1)+ 176 = -4は180 * 0 + 176 = 176と同じであるため

ここでのクロックの例を使用すると、http: //mathworld.wolfram.com/Congruence.htmlは、duration_of_time mod cycle_lengthが-45分だとは言いませんが、両方の回答が基本方程式を満たしているとしても、15分と言います。


1
数論では、それは常に肯定的であるとは限りません...それらは合同クラスに分類されます。表記上の目的でそのクラスから任意の候補を自由に選択できますが、そのクラスのすべてにマップされ、特定のその他の候補を使用すると特定の問題が大幅に単純化されるという考えです(たとえば、-1代わりに選択するn-1)。それでそれを持っています。
BeUndead

2

Java 8には Math.floorModがありますが、非常に低速です(その実装には複数の除算、乗算、および条件があります)。ただし、JVMには固有の最適化されたスタブがある可能性がありますが、これにより大幅にスピードアップします。

これを使わずにこれを行う最速の方法floorModは、ここでの他のいくつかの答えのようですが、条件付きブランチがなく、1つだけ遅い%オペレーションがです。

nが正であり、xは何でもかまいません:

int remainder = (x % n); // may be negative if x is negative
//if remainder is negative, adds n, otherwise adds 0
return ((remainder >> 31) & n) + remainder;

次の場合の結果n = 3

x | result
----------
-4| 2
-3| 0
-2| 1
-1| 2
 0| 0
 1| 1
 2| 2
 3| 0
 4| 1

あなただけの間の一様分布が必要な場合0n-1、正確なMOD演算子とされていない、とあなたxのクラスタの近くにはありません0が、より命令レベルの並列性があると遅いよう、次のことが、さらに高速になります%計算は、他のと並行して行われます彼らはその結果に依存しないので、部品。

return ((x >> 31) & (n - 1)) + (x % n)

上記の結果n = 3

x | result
----------
-5| 0
-4| 1
-3| 2
-2| 0
-1| 1
 0| 0
 1| 1
 2| 2
 3| 0
 4| 1
 5| 2

入力がintの全範囲でランダムである場合、2つの解の両方の分布は同じになります。入力クラスターがゼロに近い場合、n - 1後者のソリューションでは結果が少なすぎます。


1

ここに代替があります:

a < 0 ? b-1 - (-a-1) % b : a % b

これは、他の数式[(a%b + b)%b]よりも速い場合とそうでない場合があります。他の数式とは異なり、分岐が含まれていますが、使用するモジュロ演算は1つ少なくなっています。コンピュータが<0を正しく予測できる場合は、おそらく勝利です。

(編集:数式を修正しました。)


1
ただし、剰余演算には除算が必要であり、さらに低速になる可能性があります(特に、プロセッサがほぼ常に正しく分岐を推測する場合)。だからこれはおそらくより良いです。
デイブ

@KarstenR。あなたが正しいです!数式を修正しましたが、正しく機能します(ただし、さらに2つの減算が必要です)。
ステファンライヒ

それは本当です@dave
Stefan Reich
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.