2016.02.15

double型のもつ誤差

誤差について

double型で小数点以下の値があるときに誤差が出る可能性があります。

以下の例は、double型変数に0.8、0.9、1.0を代入してprintコンソール出力したものです。

	0.8 → 0.799999999999999900000000000000
	0.9 → 0.899999999999999900000000000000
	1.0 → 0.999999999999999900000000000000

このような誤差が出る理由は、後ほど説明します。

具体的には、以下のJavaソースコードと出力結果をみてください。

Javaソースコード その1

DoubleError1.java

001
002
003
004
005
006
007
008
009
010
011
public class DoubleError1 {
	public static void main( String[] args ) {
		// 変数の宣言
		double value;

		// valueを0.0~1.0で,0.1増やしていく
		for ( value = 0.0; value <= 1.0; value += 0.1 ) {
			System.out.println( String.format("%.30f", value ) );
		}
	}
}

コンパイル ソースコードが「ANSI」の場合

C:\talavax\javasample>javac -encoding sjis DoubleError1.java

コンパイル ソースコードが「UTF-8」の場合

C:\talavax\javasample>javac DoubleError1.java

実行

C:\talavax\javasample>java DoubleError1
0.000000000000000000000000000000
0.100000000000000000000000000000
0.200000000000000000000000000000
0.300000000000000040000000000000
0.400000000000000000000000000000
0.500000000000000000000000000000
0.600000000000000000000000000000
0.700000000000000000000000000000
0.799999999999999900000000000000
0.899999999999999900000000000000
0.999999999999999900000000000000

出力結果からdouble型変数valueに誤差をもっている誤差をもっていることが分かります。結果は、double型の値を小数点以下30桁を表示しています。

0.3の場合、誤差が0.00000000000000004です。非常に小さな値ですが、プログラムの用途にとっては問題となる可能性があります。同様に0.8、0.9、1.0も誤差をもっています。

もう少し、詳しくdoubleの値を見ていきます。

Javaソースコード その2

以下のソースコードDoubleError2.javaは、DoubleError1.javaの値を詳しく見るためにBigDecimalクラスを使っています。BigDecimalクラスは、10進数値を文字列として計算するもので厳密な計算が必要なときに使用するクラスです。

ここでは、BigDecimalクラスで計算せずに、double型変数valueの値をBigDecimalに変換するために使っています。

DoubleError2.java

001
002
003
004
005
006
007
008
009
010
011
012
013
import java.math.BigDecimal;

public class DoubleError2 {
	public static void main( String[] args ) {
		// 変数の宣言
		double value;

		// valueを0.0~1.0で,0.1増やしていく
		for ( value = 0.0; value <= 1.0; value += 0.1 ) {
			System.out.println( new BigDecimal( value ) );
		}
	}
}

実行結果

コンパイル ソースコードが「ANSI」の場合

C:\talavax\javasample>javac -encoding sjis DoubleError2.java

コンパイル ソースコードが「UTF-8」の場合

C:\talavax\javasample>javac DoubleError2.java

実行

C:\talavax\javasample>java DoubleError2
0
0.1000000000000000055511151231257827021181583404541015625
0.200000000000000011102230246251565404236316680908203125
0.3000000000000000444089209850062616169452667236328125
0.40000000000000002220446049250313080847263336181640625
0.5
0.59999999999999997779553950749686919152736663818359375
0.6999999999999999555910790149937383830547332763671875
0.79999999999999993338661852249060757458209991455078125
0.899999999999999911182158029987476766109466552734375
0.99999999999999988897769753748434595763683319091796875

出力結果の0と0.5以外に誤差があることがわかります。

誤差が出る理由

ここから、誤差が出る理由について考えてみます。

コンピュータで扱う数値は2進数です。double型は、64ビットなので64個のビットで作成されている値です。

int型long型などの整数の値も2進数で作成されていますが、これらは整数なので誤差ゼロで表現できます。ただし、値の範囲はあります。

次に、小数部を2進数で表現する方法を説明します。

小数部は2-n(n>0)の数値の合計で表現されます。つまり、0.5/0.25/0.125/0.0625…の合計で小数点以下を表現します。下表は、10進数と2進数を対応させた表です。

10進数 2n 2進数
0.5 2-1 0.1
0.25 2-2 0.01
0.125 2-3 0.001
0.0625 2-4 0.0001
0.03125 2-5 0.00001

例えば、2.52進数で表すと、整数部の10、小数部の0.50.1となり、

2.5の2進表現は、

10.1

となります。

もう一つ、5.3752進数で表すと、整数部の101、小数部の0.375=0.25+0.1250.011=0.01+0.001となり、

5.375の2進表現は、

101.011

となります。

つまり、double型の小数部は2-n(n>0)の合計値で表現されており、この方式で完全に表現できない場合に誤差がでます。

ちなみに、2つの実数(double型float型)が同じかどうかを条件式で判定するときに==を使わない理由は、この誤差にあります。

以上です。

関連コンテンツ

変数やクラスに格納されている値をコンソール出力する方法は?

2020.03.23

プログラムの最初に実行されるメソッドは?

2022.12.13

プログラミングで使う変数って何?

2020.03.23

Javaのプログラムを書いてみませんか?プログラムの書き方をくわしく説明しています。

2020.03.23

「Javaソースコード」から実行可能な「オブジェクトコード」に変換する方法をくわしく説明しています。

2020.03.23

プログラミング言語とは?種類や特徴について説明しています。

2022.08.03

StringクラスとStringBuilderクラスを利用したプログラミングの仕方を紹介しています。

2016.12.16

Javaプログラムの構成について解説しています。詳しくは、こちらをご覧ください。

2020.03.23

「0」と「1」の2つの数字で表される2進数(バイナリ)。一般に使われている10進数に変換するには。

2016.02.16

数値を2進数で表したときの各桁の「0」と「1」に対して演算を行えます。4種類の演算、AND(論理積)、OR(論理和)、XOR(排他的論理和)、NOT(否定)を詳しく説明しています。

2016.03.26

コンピューター(computer)の意味を説明しています。

2022.07.22

自然数と整数って何が違う?

2020.03.23

複数の数値の合計値と平均値を計算するプログラムをJavaのソースコードを使って解説しています。

2020.03.23

条件式を判断して処理を分岐する方法を詳しく説明しています。

2023.03.20

「ゆるゆるプログラム」のコンテンツを紹介しています。興味のある方はこの記事をご覧ください。

2020.03.23

Javaの学習に役立つソースコードを多数紹介しています。是非、ご覧ください。

2022.09.10

Javaを使った簡単な応用プログラム(生年月日から年齢を計算プログラムなど)を紹介しています。

2022.07.07

Swingパッケージを使ってグラフィック表示を行う方法を解説しています。

2020.03.23

画像フォーマット形式・色・大きさ・傾きなどの変更、特定の図形(文字・記号など)を見つけたり、取り出したりする画像処理について詳しく解説。

2015.11.29

三角形、台形、円などいろいろな図形の面積を計算するプログラムを紹介しています。詳しくは、記事をご覧ください。

2021.05.18

配列を使うJavaソースコードを多数紹介しています。

2021.05.18

繰り返し処理を使ったJavaのソースコードサンプルを紹介しています。

2020.03.23

数学に関係するJavaのメソッドやソースコードなどを紹介しています。

2022.10.25

日本で使われてきた伝統文様「和柄」について解説しています。

2022.07.27

プログラミング、ITに関する用語をまとめています。

2022.10.17

2つの値を比較する方法を解説しています。与えられた2つの値が同じか、大きいか、小さいかを判定します。

2019.08.04

広告