web-dev-qa-db-ja.com

CおよびPython-モジュロ(%)演算の異なる動作

同じmod操作が、使用されている言語に応じて異なる結果を生成することを発見しました。

Pythonの場合:

-1 % 10

9を生成します

Cでは-1

  1. どれが正しいモジュロですか?
  2. Cでのmod操作をPythonと同じにする方法は?
63
psihodelia
  1. 両方のバリアントは正しいですが、数学(特に数論)では、Pythonの modulo が最も一般的に使用されます。
  2. Cでは、((n % M) + M) % Mを使用すると、Pythonと同じ結果が得られます。例: ((-1 % 10) + 10) % 10。正の整数の場合でも機能することに注意してください:((17 % 10) + 10) % 10 == 17 % 10、およびC実装の両方のバリアント(正または負の剰余)。
68
Alex B

Pythonには「真の」モジュロ演算がありますが、Cには剰余演算があります。

負の整数除算の処理方法と直接関係があります。つまり、0またはマイナス無限大に向かって丸められます。 Pythonはマイナス無限大に丸め、C(99)は0に向けますが、両方の言語では(n/m)*m + n%m == nなので、%演算子は正しい方向に補正する必要があります。

Adaはより明示的で、modremの両方を持っています。

30
fortran

C89/90では、負のオペランドを持つ除算演算子と剰余演算子の動作はimplementation-definedであり、実装に応じてどちらかの動作が得られることを意味します。演算子が互いに一致することだけが必要です:_a / b = q_から_a % b = r_は_a = b * q + r_の後に続きます。結果に批判的に依存している場合は、コードで静的アサートを使用して動作を確認します。

C99では、観察する動作が標準になりました。

実際、どちらの動作にも特定のロジックがあります。 Pythonの動作は、真のモジュロ演算を実装します。観察した動作はCであり、0への丸めと一致しています(これはFortranの動作でもあります)。

Cで0への丸めが推奨される理由の1つは、_-a / b_の結果が-(a / b)と同じになることを期待するのが自然なことです。真のモジュロ動作の場合、_-1 % 10_は9と評価され、_-1 / 10_は-1でなければなりません。 -(1 / 10)が0であるため、これはかなり不自然に見えるかもしれません。

15
AnT

-1 modulo 109 modulo 10と同じであるため、どちらの回答も正解です。

r = (a mod m)
a = n*q + r

確かに|r| < |n|であることは確かですが、rの値はわかりません。正と負の2つの答えがあります。


C89では、答えは常に正しいですが、剰余演算の正確な値(剰余と呼ばれます)は定義されていません。つまり、負の結果または正の結果のいずれかになります。 C99では、結果が定義されています。

肯定的な回答が必要な場合でも、回答が否定的であることがわかった場合は、単純に10を加算できます。

モジュロ演算子がすべての言語で同じように機能するようにするには、次のことに注意してください。

n mod M == (n + M) mod M

そして一般的に:

n mod M == (n + X * M) mod M
4
Brian R. Bondy

ユークリッド除算を実行するa = b*q + r、小数部を丸めるようなものですa/bを整数商qに変換してから、剰余rを計算します。

表示されるさまざまな結果は、商の四捨五入に使用される規則によって異なります...

ゼロに向かって丸める(切り捨てる)と、Cのようにゼロを中心とした対称性が得られます。

truncate(7/3) = 2
7 = 3*2 + 1

truncate(-7/3) = -2
-7 = 3* -2 - 1

truncate(7/-3) = -2
7 = -3* -2 + 1

負の無限大(床)に丸めると、Pythonのように余りが得られます。

floor(7/3) = 2
7 = 3*2 + 1

floor(-7/3) = -3
-7 = 3* -3 + 2

floor(7/-3) = -3
7 = -3* -3 - 2

最も近いintに丸める場合(必要なものに、偶数に、またはゼロから離れるように結び付ける)、中央に配置されたモジュロを取得します。

round(7/3) = 2
7 = 3*2 + 1

round(8/3) = 3
8 = 3*3 - 1

round(-7/3) = -2
-7 = 3* -2 - 1

round(7/-3) = -2
7 = -3* -2 + 1

正の無限大(ceil)に丸める独自のモジュロを実装しようとすると、inventかなり型破りなモジュロになりますが、それでもモジュロの一種です...

1
aka.nice

python 3.7以降、 math 組み込みモジュールから .remainder() を使用することもできます。

_Python 3.7.0a0 (heads/master:f34c685020, May  8 2017, 15:35:30)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.remainder(-1, 10)
-1.0
_

docs から:

Yに関するxのIEEE 754スタイルの剰余を返します。有限xおよび有限非ゼロyの場合、これは_x - n*y_の差です。ここで、nは商_x / y_の正確な値に最も近い整数です。 _x / y_が2つの連続した整数のちょうど中間にある場合、最も近い偶数の整数がnに使用されます。したがって、残りのr = remainder(x, y)は常にabs(r) <= 0.5 * abs(y)を満たします。

特別な場合はIEEE 754に従います。特に、remainder(x, math.inf)は有限のxの場合はxであり、remainder(x, 0)remainder(math.inf, x)はNaN以外のxの場合はValueErrorを発生させます。剰余演算の結果がゼロの場合、そのゼロはxと同じ符号になります。

IEEE 754 2進浮動小数点を使用するプラットフォームでは、この演算の結果は常に正確に表現可能です。丸め誤差は発生しません。

0
vishes_shell