初心者の時につまずいた除算の動作をJava言語仕様で確認
あいさつに代えて
7月末に福岡県に行ってきました。帰りに乗るはずだった飛行機が欠航になった為、代わりの飛行機に乗るまで半日時間が空くことになったので、JR博多シティにある映画館で過ごしました。
今回の画像は、映画館にある待合スペースの「トレインビュー」から撮影したものです。
Java言語仕様とは
Java言語仕様(The Java Language Specification)とは、その名の通りJava言語の言語仕様について記載されたものです。こちらのページに、JavaVM仕様(The Java Virtual Machine Specification)と共に、各バージョン毎に置かれています。
先日リリースされたバージョン23についても公開されていますが、今回の説明ではLTS版のバージョン21を使います。
Java言語仕様で動作を確認する
今回は、次の動作をJava言語仕様で確認したいと思います。
「整数同士を除算した結果は整数で戻る」
昔々、私がJavaで初めて本格的なプログラムを作成した際に、つまずいた動作がありました。それは、「整数同士を除算した結果は整数で戻る」事でした。
例えば、次の様な計算です。
int i = 22;
int j = 7;
double result1 = i / j; // JShellでの結果は3.0
double result2 = (double)i / j; // JShellでの結果は3.142857142857143
整数(intやlong)同士を除算した結果を小数点以下まで求めたい場合は、どちらかの値を実数(doubleやfloat)に変換する必要があるのですが、これにつまずきました。
除算演算子を確認
Java言語仕様では、除算演算子は「15.17.2. Division Operator /」に記載があります。この中で、「整数の除算は0に丸められる」(Integer division rounds toward 0.)とありますので、整数同士の除算した結果は整数になります。
あっさり確認は終わってしまいました、、、。
整数の除算となる条件を確認
ここでは少し掘り下げて、整数の除算となる条件を確認します。
乗算・除算・剰余の演算について、整数・浮動小数(実数)のどちらの演算が選ばれるかは「15.17. Multiplicative Operators」に記載があり、「the promoted type」(昇格された型)によって決定します。
「the promoted type」がintかlongの場合、整数の演算
「the promoted type」がfloatかdoubleの場合、浮動小数(実数)の演算
また、「the promoted type」の選び方は「5.6. Numeric Contexts」に記載があります。
「expression」(以降、値とします)が参照型の場合、プリミティブに変換(unboxing conversion)
次の条件により「the promoted type」を決定し、値を変換
いずれかの値がdoubleの場合、「the promoted type」はdoubleとし、doubleでない値をdoubleに変換
それ以外でいずれかの値がfloatの場合、「the promoted type」はfloatとし、floatでない値をfloatに変換
それ以外でいずれかの値がlongの場合、「the promoted type」はlongとし、longでない値をlongに変換
それ以外の場合、「the promoted type」はコンテキストの種類によって決定。乗算・除算・剰余の演算は「numeric arithmetic context」に当たる為、「the promoted type」はintとし、intでない値をintに変換
演算結果を確認
演算の結果については、演算の種類によって異なります。
整数の演算の場合、「4.2.2. Integer Operations」に記載があります。
いずれかの値がlongの場合、演算は64ビット精度で行い、結果はlongで返す
それ以外の場合、演算は32ビット精度で行い、結果はintで返す
浮動小数(実数)の演算の場合、「4.2.4. Floating-Point Operations」に記載があります。
いずれかの値がdoubleの場合、演算は64ビット浮動小数点演算で行い、結果はdoubleで返す
それ以外(でいずれかの値がfloat)の場合、演算は32ビット浮動小数点演算で行い、結果はfloatで返す
実際に計算して確認
ここまで調べた内容を確認する為に、実際に計算して確認したいと思います。JShellを使うと、簡単に結果を確認できます。
JShellの/openコマンドで指定するファイルは、次になります。
var nn1 = 22 / 7;
var nn2 = 22 / 7L;
var nn3 = 22 / 7f;
var nn4 = 22 / 7d;
var nn5 = 22L / 7;
var nn6 = 22L / 7L;
var nn7 = 22L / 7f;
var nn8 = 22L / 7d;
var nn9 = 22f / 7;
var nn10 = 22f / 7L;
var nn11 = 22f / 7f;
var nn12 = 22f / 7d;
var nn13 = 22d / 7;
var nn14 = 22d / 7L;
var nn15 = 22d / 7f;
var nn16 = 22d / 7d;
JShellの/varsコマンドで、実行結果を確認できます。
jshell> /vars
| int nn1 = 3
| long nn2 = 3
| float nn3 = 3.142857
| double nn4 = 3.142857142857143
| long nn5 = 3
| long nn6 = 3
| float nn7 = 3.142857
| double nn8 = 3.142857142857143
| float nn9 = 3.142857
| float nn10 = 3.142857
| float nn11 = 3.142857
| double nn12 = 3.142857142857143
| double nn13 = 3.142857142857143
| double nn14 = 3.142857142857143
| double nn15 = 3.142857142857143
| double nn16 = 3.142857142857143