Java/基礎/算術演算
情報技術 > Java > Java/基礎/算術演算
計算をしてみよう[編集]
Hello worldを少しだけ発展させて、プログラムに計算をさせてみましょう。 といってもやはり現段階ではコンソールに文字を出力するだけという、単なるサンプルでしかありません。 Hello world同様、プログラムに実用的な意味はありません。
public class Main { public static void main(String[] args) { System.out.println(1 + 1); } }
上記のプログラムを「Main.java」という名前で保存してください。 Hello worldと違うのは(クラス名が「Main」になっていることと)太字の部分、つまりHello worldでは「"Hello, world."」だった部分が「1 + 1」という計算式になっていることです。
Hello worldと同様の手順でコンパイルし、実行してみましょう。まずコンパイルです。ファイルを保存したディレクトリに移動して、
javac Main.java
でコンパイルできます。エラーが出なければ、実行です。
java Main
答えとして「2」が表示されれば成功です。
さて、ここで出てきた「1 + 1」の「+」の記号を「算術演算子」と呼びます。算術演算子は、その名のとおり、演算子の一種で、算術に関係したものを指します。演算子というのは演算用の記号というような意味です。
算術演算子の種類[編集]
Javaで使える算術演算子には、次のようなものがあります。
演算子 | 説明 |
---|---|
+ | 加算演算子(足し算の記号) |
- | 減算演算子(引き算の記号) |
* | 乗算演算子(掛け算の記号) |
/ | 除算演算子(割り算の記号) |
% | 剰余演算子。剰余とは、割り算の余りのことです |
算術演算の注意事項[編集]
割り算に関しては、若干注意が必要です。たとえば、
public class Main { public static void main(String[] args) { System.out.println(10 / 3); } }
10 ÷ 3の答えは3.333333……ですが、上のプログラムをコンパイルして実行してみると、結果は単なる「3」です。整数同士を除算した場合、結果は小数点以下を切り捨てた整数値になるのです。
なお、たとえば「10 / 3」の部分を「10.0 / 3」もしくは「10 / 3.0」あるいは「10f / 3」「10 / 3f」などのように書くと、小数点以下も得られます。
ただし、このようにして行った小数の計算は、しばしば微少な誤差を含みます。有名な例としては、0.1を10回足す計算です。答えは1になるはずですが、
public class NoName { public static void main(String[] args) { double d = 0.0; for (int i = 0; i < 10; i++) d += 0.1; System.out.println(d); float f = 0.0f; for (int i = 0; i < 10; i++) f += 0.1f; System.out.println(f); } }
コンパイルして実行してみると、1.0にはなりません。
$ javac NoName.java $ java NoName 0.9999999999999999 1.0000001
このように誤差が生じるのは、内部的に小数が2進数で扱われているためです(0.1は、2進数では循環小数になるため正確に表現できません)。 正しい結果を得るには、後述するjava.math.BigDecimalクラスを利用するのが一般的です。
なお、Javaに限らない一般のプログラミングでは、割り算を後回しにすることで、誤差の影響が波及・拡大することを抑えます。
カハンの総和アルゴリズム( Kahan summation algorithm )のような誤差を保証するアルゴリズムの使用も検討に値します。
- カハンの総和アルゴリズム
public class Main { public static void main(String[] args) { double d = 0.0; for (int i = 0; i < 10; i++) d += 0.1; System.out.println(d); d = 0.0; double c = 0.0; for (int i = 0; i < 10; i++) { double y = 0.1 - c; double t = d + y; c = ( t - d ) - y; d = t; } System.out.println(d); float f = 0.0f; for (int i = 0; i < 10; i++) f += 0.1f; System.out.println(f); f = 0.0f; float fc = 0.0f; for (int i = 0; i < 10; i++) { float y = 0.1f - fc; float t = f + y; fc = ( t - f ) - y; f = t; } System.out.println(f); } }
$ javac Main.java $ java Main 0.9999999999999999 1.0 1.0000001 1.0
- Stream APIを使った総和
import java.util.stream.DoubleStream; class Main { public static void main(String[] args) { final var builder = DoubleStream.builder(); for (int i = 0; i < 10; i++) builder.add(0.1); System.out.println(builder.build().sum()); } }
1.0
- DoubleStreamのメソッドsum()は、保証を行うアルゴリズムを採用しています。
また、大きな数になる計算を行った場合、正しくない答えが表示されることがあります。
たとえば111111111 × 111111111の答えは12345678987654321のはずですが、
public class Main { public static void main(String[] args) { System.out.println(111111111 * 111111111); } }
をコンパイル、実行すると、間違った答えが表示されると思います。
この場合、太字の部分をたとえば「111111111L * 111111111」のように「L」を付けて記述すれば、正しい結果になります。 これは数値リテラルの型が int ではなく、long になるためで、これにより桁あふれが回避できました。
演算の優先順位[編集]
演算の優先順位は、ほぼ数学と同様です。たとえば、加算・減算よりも乗算・除算のほうが優先されます。
優先順位は、括弧を付けて指定することができます。これも数学同様です。ただし、使える括弧は小括弧「( )」のみで、たとえ括弧が入れ子になる場合でも大括弧や中括弧は使いません。たとえば、次のように記述します。
System.out.println((3 + (1 + 2) * 10) / (7 - 4) - 1);