web-dev-qa-db-ja.com

負数のモジュロ演算

Cプログラムで私は以下の操作を試みていた

 x = 5 % (-3);
 y = (-5) % (3);
 z = (-5) % (-3); 

printf("%d ,%d ,%d", x, y, z); 

gccで(2, -2 , -2)として出力するようにしました。毎回良い結果を期待していました。係数は負になることがありますか?誰もがこの振る舞いを説明できますか?

162
Alva

C99では、a/bが表現可能な場合はが必要です。

(a/b) * b+a%baと等しくなります

これは論理的には理にかなっています。そうですか。

これが何につながるのか見てみましょう。


例A. 5/(-3)-1

=> (-1) * (-3)+5%(-3) = 5

これは5%(-3)が2の場合にのみ発生します。


例B. (-5)/3-1

=> (-1) * 3+(-5)%3 = -5

これは、(-5)%3-2の場合にのみ発生します。

138
ArjunShankar

Cの%演算子はモジュロ演算子ではなく、剰余演算子です。

モジュロ演算子と剰余演算子は、負の値に関して異なります。

剰余演算子の場合、結果の符号は被除数の符号と同じですが、モジュロ演算子の場合、結果の符号は除数と同じです。

Cは%に対するa % b操作を次のように定義します。

  a == (a / b * b) + a % b

/0に向かって切り捨てられた整数除算です。 0を剰余演算子としてではなく剰余演算子として定義するのは、%に対して(負の無限大方向に対してではなく)行われる切り捨てです。

120
ouah

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

私の結論は、C内の(a%b)は剰余演算子で、NOTモジュロ演算子です。

57
dewang

負数かどうかをチェックする必要はないと思います。

正の法を見つけるための簡単な関数はこれでしょう -

int modulo(int x,int N){
    return (x % N + N) %N;
}

Nが正であると仮定すると、これは正と負の両方 xの値に対して機能します。

P.SあなたのxとNがそれぞれINT_MAX-1とINT_MAXのようなものに達する可能性がある場合は、intlong long intに置き換えてください。

そして、それらが長すぎる限度(つまりLLONG_MAX付近)をまたいでいる場合は、ここで他の答えで説明されているように、正と負のケースを別々に処理する必要があります。

43

他の答えはC99以降、負のオペランドを含む整数の除算は常にゼロ方向に切り捨てで説明されています。

C89では、結果が上向きまたは下向きに丸められるかどうかは実装によって定義されています。 (a/b) * b + a%bはすべての規格でaと等しいので、負のオペランドを含む%の結果もC89で実装定義です。

7
Yu Hao

係数は負になることがありますか?

%は、 剰余演算子 、除算後ではなく除算後の剰余 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   
5
chux

モジュロ演算の結果は分子の符号に依存するので、yに対して-2が得られます。 z

これが参照です

http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_14.html

整数部

整数除算を実行するための関数について説明します。 GNU Cでは、 '/'演算子は常にゼロに丸められるため、これらの関数はGNU Cライブラリでは冗長です。しかし、他のCの実装では、 '/'は負の引数で丸め方が異なる場合があります。 divとldivは、商をどのように丸めるかを指定するので便利です。残りは分子と同じ符号を持ちます。

3
Kartik Anand

これらの規約が由来する数学では、モジュロ算術が肯定的な結果をもたらすべきであるという主張はありません。

例えば。

1 mod 5 = 1ですが、-4と等しくすることもできます。つまり、1/5は0からの剰余1、または5からの-4をもたらします。(両方の要素が5)

同様に、-1 mod 5 = -1ですが、4と等しくすることもできます。つまり、-1/5は、0から-1、または-5から4の余りになります。 (5の両方の要因)

さらに読むために、数学の中の 同値類 を調べてください。

2
DarkPurple141

C99標準 、section6.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乗法演算子

構文

  1. 乗法式:
    • cast-expression
    • multiplicative-expression * cast-expression
    • multiplicative-expression / cast-expression
    • multiplicative-expression % cast-expression

制約

  1. 各オペランドは算術型を持ちます。 演算子のオペランドは整数型になります。

意味論

  1. 通常の算術変換はオペランドに対して実行されます。

  2. 二項*演算子の結果は、オペランドの積です。

  3. /演算子の結果は、最初のオペランドを2番目のオペランドで除算した商です。 演算子の結果は余りです。どちらの演算でも、2番目のオペランドの値がゼロの場合、動作は未定義です。

  4. 整数が除算されると、/演算子の結果は、小数部分が切り捨てられた代数商になります[1]。商a/bが表現可能な場合、式(a/b)*b + a%baと等しくなります。

[1]:これはよく「ゼロ方向への切り捨て」と呼ばれます。

モジュラス演算子は余りを与えます。 cのモジュラス演算子は通常、分子の符号を取ります

  1. x = 5%(-3) - ここで分子は正であるため、2になります。
  2. y =(-5)%(3) - ここで分子は負であるため、結果は-2
  3. z =(-5)%(-3) - ここで分子は負であるため、結果は-2

また、モジュラス(剰余)演算子は整数型でのみ使用でき、浮動小数点では使用できません。

1
Kavya

モジュロ演算子は、数値が正の場合はmod演算子とまったく同じですが、数値が負の場合は異なります。

問題の多くの場合、10 ^ 9 + 7を法とする答えを与えるように求められます。

(モジュロを使う前の)答えを 'a'で表すことにしましょう。

単純明快なルール -

aがの場合、モジュロ10 ^ 9 + 7 = a%(10 ^ 9 + 7)

aがの場合、モジュロ10 ^ 9 + 7 = (a%(10 ^ 9 + 7))+(10 ^ 9) +7)

このような問題で、ループの任意のステップが整数範囲外の値を計算する可能性がある場合(整数を使用している場合)、そのステップ自体でモジュロ演算子を使用できます。最終的な答えは、モジュロ演算子を一度だけ使用したかのようになります。

これは、 - (a * b)%c =((a%c)(b%c))%cであるからです。

0
Whiplash99

modは抽象算術で定義されているので、考えるほうが便利だと思います。演算としてではなく、さまざまな要素やさまざまな演算子を持つ、まったく異なるクラスの算術演算として。これはmod 3の加算が「通常の」加算と同じではないことを意味します。あれは;整数加算.

だからあなたがするとき:

5 % -3

integer 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ではありません。それは残りの部分です。とにかく、ポイントは否定を返すことは意味があるということです。

0
FelipeC