Java/基礎/算術演算

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

情報技術 > 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);
このページ「Java/基礎/算術演算」は、まだ書きかけです。加筆・訂正など、協力いただける皆様の編集を心からお待ちしております。また、ご意見などがありましたら、お気軽にトークページへどうぞ。