はじめに
きっかけから。
競プロの勉強をしている際に以下のようなコード(Javaです)を書きました。
double ans = 1/3 * 5;
System.out.println(ans);
//出力結果は0.0
//自分が出力させたかった答えは1.6666・・・
double型で変数を宣言しているのだから答えも小数になるはずだ、と思っていたので、
想定外の出力結果にかなり混乱しました。
なぜこうなったのか、自分が思ったとおりに小数で出力させるためにはどうしたらいいのかを
自分なりに考えて試した結果を以下にまとめます。
試して考えてみた
かけ算で答え0になるからには、どこかが0と計算されているはずだと考え、
ただの1/3を出力させたところ結果は0.
System.out.println(1/3); //0
ここに問題があったのかと嬉しくなりましたね。
これをもとにいろいろ試した結果を以下に記します。
System.out.println(1.0/3.0); //0.3333…
System.out.println(5*1/3); //1
System.out.println(5.0*1/3); //1.6666…67
System.out.println(1/3*5.0); //0.0
System.out.println((1/3)*5); //0
System.out.println((1/3)*5.0); //0.0
System.out.println((1.0/3.0)*5.0);//1.6666…5
System.out.println((1.0/3.0)*5);//1.6666…5
System.out.println(1.0/3); //0.3333…
System.out.println(1/3.0); //0.3333…
double t1 = 1.0/3;
System.out.println(t1);//0.3333
t1 = 1/3.0;
System.out.println(t1);//0.3333
まず、これらの結果から計算式に小数を一つでも含めていれば出力結果も小数の形式になるということに気が付きました。
式の結果が0になるのは1/3を整数型としてとらえているので、0という結果になるんだろうと予想しています。
次に、分数というか割り算部分の処理について考えます。
こちらも、分母か分子のどちらかに小数が含まれていれば小数になるようです。
さらに、()付の計算式について。
()の中身が先に計算される、というのは自明でしょうが、
()の中身の計算結果が一つの数字として扱われているようだ、ということは個人的に新しい発見でした。
コードは前から処理されるものだから、考えてみればその通りなんですけどね。
以上のことから、
Javaの数字リテラルはその書き方によって整数型か小数型か分けられるのだろうと思いました。
調べてみた
検索して調べてみたところ、
コード中に直接書くもののことをリテラルと呼ぶこと。
数値リテラルには、整数リテラル(指定しなければint)と浮動小数点リテラル(指定しなければdouble)の2種類があること。
がわかりました。
リテラルについてのこういう説明は以前も読んだことはありましたが、実際にこのような動作になるとは理解しきれていませんでした。
説明を読むときもただ説明を受け取るだけでなく、考え抜くことが大切ですね。
数値リテラルを使うときには、整数であれ小数であれすべての項で型をそろえるほうが良さそうです。
参考サイト:
Java|数値リテラルにサフィックスを付けて型を指定する
とほほのJava入門
あと、これは調べたときの発見なのですが、
数値リテラル内にアンダーバーを入れられるそうです。
桁の境目にアンダーバーを入れて読みやすくするためなどに使うようです。
数値リテラル内のアンダースコア|Oracle
終わりに
最初に書いた1/3*5
が想定通りにならなかったのは、
すべて整数リテラルで書いていたから、というわけでした。
単純に自分がリテラルについて理解していなかっただけでした。
たぶんもう同じ間違いはしないでしょうが、備忘録として残しておきます。
私はまだまだ知識不足ですので、何か間違ったことを書いていればご指摘お願いします。
おまけ
System.out.println(5.0*1/3); //1.6666…67
System.out.println((1.0/3.0)*5.0);//1.6666…5
System.out.println((1.0/3.0)*5);//1.6666…5
こちらの3行。見てわかる通り、最下位の値が異なるんですよね。
いったいこれはなぜなのか。
1行目は5.0÷3
2,3行目は、0.333…÷5
2,3行目に無限小数が含まれていることが原因なのでしょうか?
さくっと調べた印象では、やはり無限小数が絡んでいそうです。
無限小数は近似値で格納されるため計算結果に誤差が出るのだそう。
この誤差を生まないためのライブラリもあるようですが、無限に続くものを正確に計算するってどういった仕組みになっているんでしょう。気になるところです。