私がテストした言語では、- (x div y )
は-x div y
と等しくありません。 Pythonで//
、Rubyで/
、Perl 6でdiv
をテストしました。 Cも同様の動作をします 。
div
は通常 除算の結果の丸めdownとして定義されるため、その動作は通常仕様に従っています。 、ただし、div
が符号に応じて異なる方法で動作し、 thisなどの混乱を引き起こすため、算術的な観点からはあまり意味がありませんPythonでどのように行われるかについて投稿してください 。
この設計決定の背後にある特定の理論的根拠はありますか、それともdiv
はそのように最初から定義されていますか?どうやら Guido van Rossumはコヒーレンシ引数を使用します Pythonでそれがどのように行われるかを説明するブログ投稿でですが、切り上げを選択した場合もコヒーレンシを持つことができます。
( #Perl6のPMuriasによるこの質問IRCチャネル に触発された)
浮動小数点演算は、数値アプリケーションを考慮してIEEE754によって定義され、デフォルトでは、非常に厳密に定義された方法で最も近い表現可能な値に丸められます。
コンピューターでの整数演算は、一般的な国際標準によってnot定義されています。言語(特にCファミリーのもの)によって許可される操作は、基礎となるコンピューターが提供するものすべてに従う傾向があります。一部の言語は特定の操作を他の言語よりも堅牢に定義しますが、当時の利用可能な(および一般的な)コンピューターでの実装が過度に困難または遅いことを避けるため、その動作に非常に厳密に従う定義を選択します。
このため、整数演算は、オーバーフロー(加算、乗算、および左シフトの場合)でラップアラウンドし、負の無限大に向かってラウンドする傾向があります不正確な結果を生成する場合(除算および右シフトの場合)。 これらは両方とも単純なtruncation整数のそれぞれの端で 2の補数のバイナリ算術;コーナーケースを処理する最も簡単な方法。
他の回答では、言語が除算とともに提供する剰余演算子またはモジュラス演算子との関係について説明します。残念ながら、彼らはそれを後方に持っています。 剰余は除算の定義に依存し、その逆ではありません、モジュラスは除算とは無関係に定義できます-両方の引数が正であり、除算が切り捨てられた場合、同じになる人々はめったに気づかない。
最新の言語のほとんどは、剰余演算子またはモジュラス演算子のいずれかを提供しますが、両方を使用することはほとんどありません。ライブラリ関数は、差を気にする人に他の操作を提供できます。つまり、剰余は被除数の符号を保持し、モジュラスは除数の符号を保持します。
理想的には、それぞれの_b>0
_に対して、2つの操作div
とmod
を満足させたいと思います。
(a div b) * b + (a mod b) = a
_0 <= (a mod b) < b
(-a) div b = -(a div b)
_ただし、これは数学的に不可能です。上記がすべて当てはまる場合、
_1 div 2 = 0
1 mod 2 = 1
_
これは(1)と(2)に対する一意の整数解であるためです。したがって、(3)によって、
_0 = -0 = -(1 div 2) = (-1) div 2
_
(1)により、
_-1 = ((-1) div 2) * 2 + ((-1) mod 2) = 0 * 2 + ((-1) mod 2) = (-1) mod 2
_
(2)と矛盾する_(-1) mod 2 < 0
_を作成します。
したがって、(1)、(2)、および(3)の間でいくつかのプロパティを放棄する必要があります。
いくつかのプログラミング言語は放棄し(3)、div
を切り捨てます(Python、Ruby)。
一部の(まれな)場合、言語は複数の部門演算子を提供します。たとえば、Haskellでは、Pythonと同様に、(1)と(2)だけを満たす_div,mod
_があり、(1)と(3)だけを満たす_quot,rem
_もあります。後者の演算子のペアは、負の剰余を返す代償として除算ゼロに向かってを丸めます。たとえば、_(-1) `quot` 2 = 0
_と_(-1) `rem` 2 = (-1)
_があります。
C#も(2)を放棄し、_%
_が負の剰余を返すことを許可します。コヒーレントに、整数除算はゼロに向かって丸めます。 C99から始まるJava、Scala、Pascal、およびCもこの戦略を採用しています。
ウィキペディアにはこれに関する素晴らしい記事があります 、歴史と理論を含みます。
言語がユークリッドの除算プロパティを満たす限り、(a/b) * b + (a%b) == a
、フローリング除算と切り捨て除算の両方が一貫性があり、算術的に賢明です。
もちろん、人々は一方が明らかに正しく、他方が明らかに間違っていると主張するのが好きですが、賢明な議論よりも聖戦の性格があり、通常は何よりも初期の優先言語の選択に関係していますその他。また、主に彼らが選んだ%
、おそらく/
最初に%
一致します。
%
除数の記号に従うことは、明らかに全生徒の約70%が推測することですmod
ではなくmodulo
またはremainder
として読み込まれます。modulo
ではなくremainder
として読み込まれます(実際にagainstを主張しているにもかかわらず)。mod
ではなくmodulo
から読み取られるため、Fortranの区別に従う必要があります。 (これはばかげているように聞こえるかもしれませんが、C99のクリンチャーだったかもしれません。 このスレッド を参照してください。)/
床に応じて、または記号に応じて切り捨てられるため、%
は決して負ではありません):mod
に誰も驚かないと主張した。多くの言語が両方を提供します。通常、Ada、Modula-2、一部のLisp、Haskell、およびJuliaと同様に、Pythonスタイルの演算子にはmod
、C++スタイルの演算子にはrem
に関連する名前を使用します。しかし、常にではありません。たとえば、Fortranは同じものをmodulo
とmod
で呼び出します(C99について前述したとおり)。
Python、Tcl、Perl、およびその他の有力なスクリプト言語が主にフローリングを選択した理由はわかりません。質問で述べたように、Guido van Rossumの答えは、彼が3つの一貫した答えの1つを選択する必要がある理由を説明するだけであり、彼がしたものを選んだ理由ではありません。
しかし、Cの影響が重要だったと思います。ほとんどのスクリプト言語は(少なくとも最初は)Cで実装されており、Cからオペレーターインベントリを借用しています。C89の実装定義%
は明らかに壊れており、TclやPythonのような「フレンドリーな」言語には適していません。 Cは演算子「mod」を呼び出します。したがって、それらは剰余ではなくモジュラスで行きます。
1.質問の内容にかかわらず、そしてそれを引数として使用している多くの人々にもかかわらず、Cは実際にはないPython and friends。C99はフロアリングではなく除算を切り捨てる必要があります。C89でもmodのいずれかのバージョンが許可され、どちらのバージョンも許可されます。壊れた。
整数除算の意味は、完全な答えに剰余が含まれることだからです。
ポーラが言ったように、それは残りのためです。
アルゴリズムは、 ユークリッド除算 に基づいています。
Rubyでは、次のように一貫して配当を再構築できます。
puts (10/3)*3 + 10%3
#=> 10
実生活でも同じように機能します。リンゴ10個と3人。 Ok 1つをカットできますAppleは3つになりますが、設定された整数の範囲外になります。
負の数を使用すると、一貫性も維持されます。
puts (-10/3)*3 + -10%3 #=> -10
puts (10/(-3))*(-3) + 10%(-3) #=> 10
puts (-10/(-3))*(-3) + -10%(-3) #=> -10
商は常に切り捨てられ(負の軸に沿って切り捨てられます)、リマインダーは次のとおりです。
puts (-10/3) #=> -4
puts -10%3 #=> 2
puts (10/(-3)) #=> -4
puts 10%(-3) # => -2
puts (-10/(-3)) #=> 3
puts -10%(-3) #=> -1