Cプログラムで私は以下の操作を試みました(動作を確認するだけです)
x = 5 % (-3);
y = (-5) % (3);
z = (-5) % (-3);
printf("%d ,%d ,%d", x, y, z);
(2, -2 , -2)
gccのように出力しました。毎回良い結果を期待していた。係数は負にできますか?誰かがこの動作を説明できますか?
Cプログラムで私は以下の操作を試みました(動作を確認するだけです)
x = 5 % (-3);
y = (-5) % (3);
z = (-5) % (-3);
printf("%d ,%d ,%d", x, y, z);
(2, -2 , -2)
gccのように出力しました。毎回良い結果を期待していた。係数は負にできますか?誰かがこの動作を説明できますか?
回答:
C99 では、が次の場合a/b
に表現可能であることが必要です。
(a/b) * b
+ a%b
等しいa
これは論理的に理にかなっています。正しい?
これが何につながるか見てみましょう:
例A 5/(-3)
は-1
=> (-1) * (-3)
+ 5%(-3)
=5
これ5%(-3)
は、2の場合にのみ発生します。
例B (-5)/3
は-1
=> (-1) * 3
+ (-5)%3
=-5
これが発生するの(-5)%3
は、-2
-5/3
は-2
、modは1になります。要するに、1つのモジュールは被除数記号(切り捨て)に続く記号を持ち、もう1つのモジュールは除数記号(Knuth)に続く記号を持ちます。
%
C の演算子はモジュロ演算子ではなく、剰余演算子です。
モジュロ演算子と剰余演算子は、負の値に関して異なります。
剰余演算子では、結果の符号は被除数の符号と同じですが、モジュロ演算子では、結果の符号は除数と同じです。
Cは次のように%
操作を定義しますa % b
。
a == (a / b * b) + a % b
/
向かって切り捨て付き整数の除算0
。これ0
は、%
を剰余演算子ではなく剰余演算子として定義する(負の無限大ではなく)に対して行われる切り捨てです。
remainder
とmodulo
スキームに、rem
そしてmod
Haskellで)。これらの演算子の仕様は、除算の方法、つまり0または負の無限大への切り捨てについて、これらの言語で異なります。ちなみに、C標準で%
はモジュロ演算子を呼び出すことはなく、単に%演算子と名付けてい ます。
remainder
関数と混同しないでください。この関数は、部門で最も近い丸めのセマンティクスでIEEE剰余を実装します
C99仕様に基づく: a == (a / b) * b + a % b
計算する関数を書くことができます(a % b) == a - (a / b) * b
!
int remainder(int a, int b)
{
return a - (a / b) * b;
}
モジュロ演算の場合、次の関数を使用できます(を想定b > 0
)
int mod(int a, int b)
{
int r = a % b;
return r < 0 ? r + b : r;
}
私の結論はa % b
、Cでは剰余演算であり、剰余演算ではないということです。
b
が負の場合、これは正の結果を与えません(実際にはr
、b
両方の負の場合、結果は未満になります-b
)。使用できるすべての入力で確実な結果を得るために、r + abs(b)
またはb
s記号と一致させるために、r*b < 0
代わりに条件を変更できます。
r + abs(b)
はUB b == INT_MIN
です。
数値が負かどうかを確認する必要はないと思います。
正のモジュロを見つける簡単な関数は次のようになります-
編集:仮定N > 0
とN + N - 1 <= INT_MAX
int modulo(int x,int N){
return (x % N + N) %N;
}
これは、xの正の値と負の値の両方で機能します。
オリジナルPS:また@chuxによって尖ったアウトなど、あなたのxおよびNは、それぞれINT_MAX-1とINT_MAXのようなものに達するだけで交換することができる場合int
でlong long int
。
また、それらがlong longの制限を超えている場合(つまり、LLONG_MAXに近い場合)、ここで他の回答で説明されているように、正のケースと負のケースを別々に処理する必要があります。
N < 0
、結果はのように負になる可能性があることに注意してくださいmodulo(7, -3) --> -2
。未定義の動作である数学をx % N + N
オーバーフローさせることもできint
ます。たとえばmodulo(INT_MAX - 1,INT_MAX)
、-3になる可能性があります。
long long int
、単にを使用するか、または(単純さを失うことを犠牲にして)否定的なケースを個別に処理します。
係数は負にできますか?
%
Euclidean_divisionの後ではなく、除算後の剰余演算子であるため、負の値にすることができます。C99以降、結果は0、負または正になる場合があります。
// a % b
7 % 3 --> 1
7 % -3 --> 1
-7 % 3 --> -1
-7 % -3 --> -1
モジュロたかったOPは、古典的であるユークリッド法として、ではありません%
。
毎回良い結果を期待していた。
定義されるたびa/b
に明確に定義されるユークリッドモジュロを実行するにa,b
は、符号があり、結果が負になることはありません。
int modulo_Euclidean(int a, int b) {
int m = a % b;
if (m < 0) {
// m += (b < 0) ? -b : b; // avoid this form: it is UB when b == INT_MIN
m = (b < 0) ? m - b : m + b;
}
return m;
}
modulo_Euclidean( 7, 3) --> 1
modulo_Euclidean( 7, -3) --> 1
modulo_Euclidean(-7, 3) --> 2
modulo_Euclidean(-7, -3) --> 2
Modulo演算の結果は分子の符号に依存するため、yおよびzに対して -2が得られます。
これがリファレンスです
http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_14.html
整数部
このセクションでは、整数除算を実行するための関数について説明します。GNU Cでは「/」演算子は常にゼロに丸められるため、これらの関数はGNU Cライブラリでは冗長です。ただし、他のCの実装では、「/」は負の引数で異なる丸めが行われる場合があります。divおよびldivは、商をゼロに丸める方法を指定するので便利です。残りは分子と同じ符号を持っています。
これらの慣習が由来する数学では、剰余算術が肯定的な結果をもたらすはずであるという主張はありません。
例えば。
1 mod 5 = 1ですが、-4にすることもできます。つまり、1/5は0から余り1を、5から-4を返します(両方の係数5)。
同様に、-1 mod 5 = -1ですが、4に等しくすることもできます。つまり、-1 / 5は、0からの剰余-1または-5からの剰余-1を生成します。(5の両方の要因)
詳細については、数学の等価クラスを参照してください。
a
とb
、があるとしb <> 0
ます。ユークリッド除算の定理によれば、整数の正確に一つのペアが存在するm
、とします。これは、(数学的な)モジュロ演算の結果であり、定義により負ではありません。ウィキペディアの詳細とリンク:en.wikipedia.org/wiki/Euclidean_divisionr
a = m * b + r
0 <= r < abs( b )
r
1 mod 5
は常に1です。1でもある-4 mod 5
可能性がありますが、それらは異なるものです。
C99標準、セクション6.5.5乗法演算子によれば、以下が必要です。
(a / b) * b + a % b = a
C99によれば、剰余演算の結果の符号は被除数の符号と同じです。
いくつかの例を見てみましょう(dividend / divisor
):
(-3 / 2) * 2 + -3 % 2 = -3
(-3 / 2) * 2 = -2
(-3 % 2) must be -1
(3 / -2) * -2 + 3 % -2 = 3
(3 / -2) * -2 = 2
(3 % -2) must be 1
(-3 / -2) * -2 + -3 % -2 = -3
(-3 / -2) * -2 = -2
(-3 % -2) must be -1
6.5.5乗法演算子
構文
- 乗法式:
cast-expression
multiplicative-expression * cast-expression
multiplicative-expression / cast-expression
multiplicative-expression % cast-expression
制約
- オペランドのそれぞれは、算術型を持つ必要があります。%演算子のオペランドは整数型でなければなりません。
意味論
通常の算術変換は、オペランドに対して実行されます。
二項*演算子の結果は、オペランドの積です。
/演算子の結果は、最初のオペランドを2番目のオペランドで除算した商です。%演算子の結果が余りです。どちらの演算でも、第2オペランドの値がゼロの場合の動作は未定義です。
整数を除算すると、/演算子の結果は、小数部分が破棄された代数商になります[1]。商
a/b
が表現可能な場合、式(a/b)*b + a%b
はと等しくなりa
ます。[1]:これはしばしば「ゼロへの切り捨て」と呼ばれます。
考える方が便利だと思います mod
抽象算術で定義されているので。演算としてではなく、異なる要素と異なる演算子を持つ、まったく異なるクラスの算術として。つまり、mod 3
は「通常の」追加と同じはありません。あれは; 整数加算。
だからあなたがするとき:
5 % -3
整数 5をのセットの要素にマップしようとしていますmod -3
。これらはの要素ですmod -3
:
{ 0, -2, -1 }
そう:
0 => 0, 1 => -2, 2 => -1, 3 => 0, 4 => -2, 5 => -1
なんらかの理由で30時間起きている必要があるとしましょう。その日の残り時間は何時間ですか。 30 mod -24
。
しかし、Cが実装しているのはでなくmod
、残りです。とにかく、ネガを返すのは理にかなっているということです。