整数除算:doubleをどのように生成しますか?


249

このコードブロックの場合:

int num = 5;
int denom = 7;
double d = num / denom;

の値はdです0.0。キャストすることで強制的に動作させることができます:

double d = ((double) num) / denom;

しかし、正しいdouble結果を得る別の方法はありますか?何が起こるかを知っているプリミティブをキャストするのは好きではありません。



9
「int」をdoubleにキャストしても安全です。精度を失うことなく、常に同じ値を取得します。
Peter Lawrey 2010年

1
コンパイラが除算のために取った正しい手順が次のとおりかどうかを知りたいです。1)numをfloatにキャスト2)denomをfloatにキャスト2)numをdenomで除算。私が間違っている場合はお知らせください。
mannyee 2014年

回答:


156
double num = 5;

それはキャストを避けます。しかし、キャスト変換は明確に定義されていることがわかります。推測する必要はありません。JLSを確認してください。intからdoubleへの変換は拡大されます。§5.1.2から:

プリミティブ変換を拡大しても、数値の全体的な大きさに関する情報は失われません。

[...]

intまたはlong値をfloatに変換したり、long値をdoubleに変換したりすると、精度が失われる可能性があります。つまり、値の最下位ビットの一部が失われる可能性があります。この場合、結果の浮動小数点値は、IEEE 754の四捨五入モード(§4.2.4)を使用して、整数値を正しく丸めたものになります。

5はdoubleとして正確に表現できます。


60
+1。キャストが怖いのはやめなさい。仕組みをご覧ください。それはすべて明確に定義されています。
Mark Peters、

1
@FabricioPHこれはすべての状況で機能し、* 1.0ソリューションと同じです。
Vitruvius 14

3
@Saposhienteそれは働く以上のものです。変数の型を変更することは、私にとって汚いコードのようです。整数でなければならない何かがあり、数学演算を実行できるようにするためだけに浮動小数点の表現を変更すると、危険を冒す可能性があります。また、一部のコンテキストでは、変数を整数として読み取ると、コードが理解しやすくなります。
Fabricio PH 2014年

2
@FabricioPHを掛けて1.0も、結果のタイプは変わります。「汚いコードのようです」は、どのシナリオで乗算を行う1.0ほうが実際に優れていると考えているのか(またはなぜそうなのか)を説明していません。
マシューFlaschen

4
@FabricioPHあなたとOPは、(double)num何らかの形で変更を行うという誤った信念(「変数の型の変更」と言う)の下で苦労しているようですnum。それはしません-それはステートメントのコンテキストのために一時的なdoubleを作成します。
Paul Tomblin、2015年

100

プリミティブのキャストの何が問題になっていますか?

何らかの理由でキャストしたくない場合は、

double d = num * 1.0 / denom;

63
...これは、乗算の前に暗黙のキャストを実行します
アリスパーセル

2
ほとんどの場合、これは他の変数のタイプを変更するよりも優れています。
Fabricio PH

3
@FabricioPH引用する情報源がない限り、これが真実であることを示唆するものはありません。
Vitruvius 14

1
@Saposhienteが他の類似のコメントで返信しました
Fabricio PH

1
「D」接尾辞を使用することもできます(同じ暗黙のキャストを実行するため)。-例:double d = num * 1D / denom;
BrainSlugs83 2014年

73

何が起こるかを知っているプリミティブをキャストするのは好きではありません。

なぜプリミティブをキャストするのに不合理な恐れがあるのですか?をにキャストしintても問題はありませんdouble。動作がわからない場合は、Java言語仕様で調べてください。inttoのキャストdoubleは、拡張プリミティブ変換です。

分子の代わりに分母をキャストすることで、括弧の余分なペアを取り除くことができます:

double d = num / (double) denom;

2
あなたもできますdouble d = (double) num / denom;...(OK、これは優先順位に依存します)
user85421

27

1つの型を変更した場合、式が変更された場合、変数が再びdoubleに忍び込むことを覚えておく必要があります。この変数が計算の一部でなくなると、結果が台無しになるためです。私は計算の中でキャストする癖をつけ、その横にコメントを追加します。

double d = 5 / (double) 20; //cast to double, to do floating point calculations

結果をキャストしてもそれはできないことに注意してください

double d = (double)(5 / 20); //produces 0.0

17

整数または整数の両方を浮動小数点にキャストして、演算を浮動小数点演算で強制的に実行します。それ以外の場合は、常に整数Mathが推奨されます。そう:

1. double d = (double)5 / 20;
2. double v = (double)5 / (double) 20;
3. double v = 5 / (double) 20;

結果をキャストしても機能しないことに注意してください。優先ルールに従って最初の除算が行われるためです。

double d = (double)(5 / 20); //produces 0.0

あなたが考えているようなキャストには問題はないと思います。


10

型キャストが唯一の方法


double整数除算からaを生成する- キャストしないと他に方法はありません(明示的には行わないかもしれませんが、起こります)。

今、私たちは正確に取得しようとすることができ、いくつかの方法がありますdouble(値numdenomしているintタイプ、のコースキャスティングとは) -

  1. 明示的なキャスト:

    • double d = (double) num / denom;
    • double d = ((double) num) / denom;
    • double d = num / (double) denom;
    • double d = (double) num / (double) denom;

だがしかし double d = (double) (num / denom);

  1. 暗黙的キャスト:

    • double d = num * 1.0 / denom;
    • double d = num / 1d / denom;
    • double d = ( num + 0.0 ) / denom;
    • double d = num; d /= denom;

でもそうでもdouble d = num / denom * 1.0;
ないdouble d = 0.0 + ( num / denom );


2

次のようなものを使用してください:

double step = 1d / 5;

(1dはdoubleへのキャストです)


5
1dはキャストではなく、単なる宣言です。
Sefier Tang 2015

0

操作をラップすることを検討してください。例えば:

class Utils
{
    public static double divide(int num, int denom) {
        return ((double) num) / denom;
    }
}

これにより、キャストが期待どおりに動作するかどうかを(一度だけ)調べることができます。このメソッドは、テストの対象となる可能性があり、希望どおりの動作を継続することを確認します。また、正しい結果が得られる限り、除算を行うためにどのトリックを使用するかは関係ありません(ここで任意の答えを使用できます)。2つの整数を除算する必要のある場所ならどこでも、呼び出しUtils::divideて、それが正しいことを信頼することができます。



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