C#の整数除算が浮動小数点数ではなく整数を返すのはなぜですか?


131

C#の整数除算が浮動小数点数ではなく整数を返す理由を誰かが知っていますか?その背後にあるアイデアは何ですか?(それはC / C ++のレガシーだけですか?)

C#の場合:

float x = 13 / 4;   
//== operator is overridden here to use epsilon compare
if (x == 3.0)
   print 'Hello world';

このコードの結果は次のようになります。

'Hello world'

厳密に言えば、整数の除算などはありません(除算は有理数を生成する演算であり、整数はその非常に小さなサブセットです)。


46
integer分割ではなくfloating point分割だからです。
ハンターマクミレン

(VB.Netでは)除算の結果がすべて無理数になる自然な数学的な方法で異なる方法で実装する必要があります。
BanditoBunny

3
私はあなたが有理数を意味すると思います。ウィキペディアを参照してください。2つの整数を除算すると、余りが生じることがあります。剰余の除算を完了するために、数値システムは、より一般的に呼ばれる分数または有理数を含むように拡張されています。
crashmstr

6
これが、私が言語の「コピー構文」のファンではない理由です。私はVB出身で、「C#は.NET」ではなく「C#は.NET」だと思っています。間違いだと思いますが、この場合はVBのほうが好きです。初期化されていない単純な型を使用しているときにコンパイラエラーを生成する問題が発生した場合(Cで警告も表示されない)、整数除算を浮動小数点数に割り当てるときに警告しないのはなぜですか。
darda 2013

回答:


96

新しいプログラマーが浮動小数点除算を実際に使用するつもりだったときに整数除算を実行するというこの間違いを犯すことはよくありますが、実際には整数除算は非常に一般的な操作です。人々がめったにそれを使用せず、除算を行うたびに常に浮動小数点にキャストすることを覚えておく必要があると想定している場合、それは間違いです。

まず、整数除算はかなり高速なので、整数の結果だけが必要な場合は、より効率的なアルゴリズムを使用することをお勧めします。

次に、整数除算を使用するアルゴリズムがいくつかあります。除算の結果が常に浮動小数点数である場合、毎回結果を丸める必要があります。私の頭の上の一例は、数値の底を変更することです。各桁の計算には、数値の浮動小数点除算ではなく、数値の整数除算と余りが含まれます。

これらの(およびその他の関連する)理由により、整数除算は整数になります。あなたは二つの整数の浮動小数点除算を取得したい場合はあなただけのキャスト1に覚えておく必要がありますdouble/ float/ decimal


5
VB.Netでは、.Netアーキテクトは別の決定を行いました。/-常に浮動小数点除算、\-整数除算なので、C ++のレガシーを考慮する場合を除いて、これは一貫性のないものです。
BanditoBunny

4
コンパイル時に、/演算子が整数除算と浮動小数点除算のどちらを実行するかを決定できます(動的を使用している場合を除く)。それが難しい場合のために、あなたはあなたがその1行にそんなにやっているので、私はとてもその簡単にオペランドが整数または浮動小数点型であるかどうかを把握することをいくつかの行にその行を分割することをお勧めしたい、それを把握します。あなたのコードの将来の読者はおそらくそれを高く評価するでしょう。
サービー

5
個人的には、分割する変数が何であるかを常に考えなければならないことが問題であり、それを注意の無駄な使用として数えています。
BanditoBunny

8
@pelesl 天文学的な数のプログラムが壊れるような大きな変更になるので、C#では絶対に起こらないと私は確信して言うことができます。それは、言語で1日目から実行する必要がある種類のことです。
サービー2013

2
@Servy:C、C ++、およびC#には、そのようなことがたくさんあります。個人的には、整数の除算に別の演算子があった場合、C#の方がより良い言語だと思います。正当なコードが驚くべき動作をもたらすのを避けるために、int/int演算子は単純に不正です[コードはオペランドをキャストするか、望まれる動作に応じて、他の演算子]。整数の除算に使用できる他の適切なトークンシーケンスがあった場合、/その目的での使用を廃止することは可能かもしれませんが、何が実用的かわかりません。
スーパーキャット2013

77

C#仕様を参照してください。除算演算子には3つのタイプがあります

  • 整数除算
  • 浮動小数点除算
  • 小数部

あなたのケースでは、次のルールが適用された整数除算があります:

除算は結果をゼロに向かって丸め、結果の絶対値は、2つのオペランドの商の絶対値よりも小さい可能な最大の整数です。2つのオペランドの符号が同じ場合、結果はゼロまたは正で、2つのオペランドの符号が反対の場合、結果はゼロまたは負になります。

C#が整数にこのタイプの除算を使用する理由(一部の言語は浮動小数点の結果を返す)はハードウェアにあると思います。整数の除算はより高速で単純です。


浮動結果を返す言語はどれですか?@SergeyBerezovskiy
Ilaria

40

各データ型は、各演算子をオーバーロードすることができます。分子と分母の両方が整数の場合、整数型は除算演算を実行し、整数型を返します。浮動小数点除算が必要な場合は、1つ以上の数値を浮動小数点型にキャストしてから除算する必要があります。例えば:

int x = 13;
int y = 4;
float x = (float)y / (float)z;

または、リテラルを使用している場合:

float x = 13f / 4f;

浮動小数点は正確ではないことに注意してください。精度が気になる場合は、代わりにdecimal型のようなものを使用してください。


1
+1は、浮動小数点除算を行うために浮動小数点である必要があるのは1つの用語だけであることを述べています。
Xynariz 2014

明らかに、正確さについてのあなたの声明は、学習の文脈において、そして理解することを複雑にしないようにすることにおいて正しいです。私たちの仕事ではできるだけ正確である必要があるので、私はまだ正確さを明確にしたいと思います:IEE 754-1985によれば、正確な結果を得ることができます(ほとんどの場合はそうではありませんが)。計算値が以前に正確に提示されていて、結果が-単純に言えば-2の累乗の合計である場合、正確な結果を得ることができます。ただし、これらの特殊なケースでその精度に依存することはベストプラクティスではありません。
L.モンティ

精度の追加:結果が1または-1に近いため、正確な結果を得る可能性が大幅に向上します。正確に表すことができる無限の数と有限の数の結果があるため、この確率がまだ0のままであることは少し混乱するかもしれません。:)
L.モンティ

1
@ L.Monty、それを取り上げてくれてありがとう。この回答を書いて以来、浮動小数点について詳しく学びました。技術的には、「浮動小数点は正確ではない」という私の声明は容認できると私は言います。何かが正確であるからといって、それが全体として正確であるとは限らない場合があるからです。彼らが言うように、壊れた時計は1日に2回は正しいですが、私は1つを精密機器と呼ぶことはありませんでした。10進数の型正確であるという私の提案よりもあなたを悩ませた部分は、実際にはかなり驚いています。
Steven Doggart

小数は、浮動小数点数と同じ理由で不正確です。浮動小数点数が2進数で10進数が10進数であることだけです。たとえば、decimal型では、1/3の正確な値を正確に保持できません。
Steven Doggart

11

接尾辞を使用しないため、リテラル134は整数として解釈されます。

マニュアル

:リテラルは接尾辞を持っていない場合は、その値を表現することができるこれらのタイプの最初のを持ってintuintlongulong

したがって、13整数として宣言するため、整数除算が実行されます。

マニュアル

x / y形式の演算では、特定の演算子の実装を選択するために2項演算子のオーバーロード解決が適用されます。オペランドは選択した演算子のパラメーター型に変換され、結果の型は演算子の戻り型になります。

事前定義された除算演算子を以下に示します。演算子はすべて、xとyの商を計算します。

整数除算:

int operator /(int x, int y);
uint operator /(uint x, uint y);
long operator /(long x, long y);
ulong operator /(ulong x, ulong y);

そして切り捨てが発生します:

除算は結果をゼロに向かって丸め、結果の絶対値は、2つのオペランドの商の絶対値よりも小さい可能な最大の整数です。2つのオペランドの符号が同じ場合、結果はゼロまたは正で、2つのオペランドの符号が反対の場合、結果はゼロまたは負になります。

次の場合:

int x = 13f / 4f;

浮動小数点除算(の/演算子13f)は浮動小数点数になり、暗黙的にintにキャストできないため、コンパイラエラーが発生します。

除算を浮動小数点除算にしたい場合は、結果を浮動小数点にする必要があります。

float x = 13 / 4;

整数を除算することに注意してください。これは暗黙的に浮動小数点にキャストされ、結果はになります3.0f接尾辞(13f4f)を使用して、オペランドを浮動小数点として明示的に宣言するには


+1は、答えを浮動小数点数としても、整数の除算を行うことができることを説明します。さらに、浮動小数点除算を強制するために私が見た別の一般的な方法は、除算の最初の項をで乗算すること1.0です。
Xynariz 14

8

そのわずか基本的な操作

あなたが分割することを学んだときに覚えておいてください。最初に解決しました9/6 = 1 with remainder 3

9 / 6 == 1  //true
9 % 6 == 3 // true

/-演算子と%-演算子を組み合わせて使用​​して、これらの値を取得します。


6

役に立つかもしれません:

double a = 5.0/2.0;   
Console.WriteLine (a);      // 2.5

double b = 5/2;   
Console.WriteLine (b);      // 2

int c = 5/2;   
Console.WriteLine (c);      // 2

double d = 5f/2f;   
Console.WriteLine (d);      // 2.5

回答の説明を追加してみてください
NetStarter

最後の式では生成され2.5ません2
Lasse V. Karlsen、2016

うん、スペルミス。ありがとう。
eozten

4

結果は常に、分子と分母の範囲が広いタイプになります。例外はバイトとショートで、int(Int32)を生成します。

var a = (byte)5 / (byte)2;  // 2 (Int32)
var b = (short)5 / (byte)2; // 2 (Int32)
var c = 5 / 2;              // 2 (Int32)
var d = 5 / 2U;             // 2 (UInt32)
var e = 5L / 2U;            // 2 (Int64)
var f = 5L / 2UL;           // 2 (UInt64)
var g = 5F / 2UL;           // 2.5 (Single/float)
var h = 5F / 2D;            // 2.5 (Double)
var i = 5.0 / 2F;           // 2.5 (Double)
var j = 5M / 2;             // 2.5 (Decimal)
var k = 5M / 2F;            // Not allowed

浮動小数点型と10進数型の間の暗黙的な変換はないため、それらの間の除算は許可されていません。どちらを使用するかを明示的にキャストして決定する必要があります(10進数は、浮動小数点型に比べて精度が高く、範囲が小さくなります)。

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