[???] /
[Java FAQ] / [S029]
S029: 浮動小数 float/double
[S029-Q01]
doubleの演算で1.1 + 2.2が 3.3にならず 3.3000000000000003 になるのはなぜですか?
[S029-A01]
丸め誤差のせいです。
double型が内部的に64ビットの浮動小数点で数値を表現している
ために「丸め」が発生しているためです。
内部の計算を2進数で行っている以上「丸め」は避ける事が
できないので、必要に応じて内部の計算を10進数で行う
java.math.BigDecimalを使いましょう。
[S029-Q02]
double, floatの計算で0になるはずのものが0にならないのはなぜですか?
[S029-A02]
丸め誤差のせいです。
通常の数値計算ルーチンは数値はすべて2進数扱いしますが、
2進数では10進数の小数を表現しきれないためにこういう現象がおこります。
2進数の桁は2の累乗扱いになるわけですが、
小数だと、2の-1乗(つまり1/2)、2の-2乗(1/4)という単位になります。
したがって、2のマイナス何乗かの数値の合成でうまく表現できない
数値は無限小数になって、最後の桁を切り捨てした部分の
誤差ができてしまいます。
対処方法としては、内部の計算を10進数で行う
java.math.BigDecimalを使いましょう。
[S029-Q03]
double の初期化がうまくいきません。どうすればいいですか?
double a = 1/1000 が 0.0 になってしまいます。
[S029-A03]
a = 1.0 / 1000.0
値を全て 小数表記しましょう。
a = 1/1000 の場合は、整数/整数の計算を行い、計算途中の結果も
整数値として扱われます。
そのため小数点以下は切り捨てられ、0 となった値を a へ代入するため 0.0 と
なってしまうわけです。
[S029-Q04]
double, float の値の比較を行いたいのですが、どのくらいの精度を持ちますか?
[S029-A04]
実数の評価は、a>b とか、a == b では行いません。
if( a == b ) の代わりに、if( abs(a-b) < 1.0e-6 )
if( a > b ) の代わりに、if( a-b > -1.0e-6 )
という風に行います。
この場合 10e-6 が、欲しい有効桁数(精度)です。
Java は IEEE 754 に基づいて数値表現がなされています。
仮数部は
single precision: 23 bits
double precision: 52 bits
有効桁数は 2進数で
single precision: 24 桁
double precision: 53 桁
とあらわされます。
[S029-Q05]
事務処理アプリケーションにおける1円未満端数処理はどうしたらよいでしょうか?
[S029-A05]
10進固定小数点パッケージ、例えば標準の BigDecimal クラスや、
IBM alphaWorks の BigDecimal パッケージを使いましょう。
http://www.alphaWorks.ibm.com/tech/BigDecimal
ただし10進固定小数点パッケージを使った場合でも有限桁なのでどこかで打ち切り
誤差は出ます。
[S029-Q06]
sqrt(-1) で Exception が発生しないのはなぜですか?
結果が sqrt:-1.#IND になってしまいます。
引数を与える前にチェックする方法はありますか?
[S029-A06]
Double.isNaN(d)
Double.isInfinite(d)
で NaN(Not a Number) かどうか調べられます。
[S029-Q07]
「NaN」って何ですか?
[S029-A07]
NaN Not a Number 非数、数でないもの)を表す値
単精度の場合 0x7fc00000 あるいは 0x7fbfffff
Inf Infinity ∞(無限大)を表す値
単精度の場合 +∞ 0x7f800000 -∞ 0xff800000
[S029-Q08]
浮動小数の表記方法を示す IEEE754 とは何ですか?
[S029-A08]
IEEE(Institute of Electrical and Electronics Engineers, Inc.)
の取り決めによる国際規格です。
浮動小数点数の表現形式の一般的なもののひとつです。
http://shop.ieee.org/store/product.asp?prodno=SS10116
上位ビットから順に
単精度 倍精度
符号(S): 1 1
指数部(E): 8 11
仮数部(M): 23 52 (bit)
で、
(-1) ^ S * (1 + M) * 2 ^ E
を表現しています。
[S029-Q09]
doubleやfloatの値を表示したとき最後に付く「E -15」という表記は何ですか?
[S029-A09]
「E -15」というのは、10の-15乗で、
4.132338098731165 * 10^-15
つまり、4.132338098731165 * Math.pow(10, -15);
または、0.000000000000004132338098731165
ということです。
[S029-Q10]
浮動小数点の値を表示するとき「E」表記を避けるにはどうすればよいですか?
[S029-A10]
DecimalFormatを使いましょう。
例)
double d = 4.132338098731165E-15;
String s = new java.text.DecimalFormat("####0.0#############################").format(d);
System.out.println(s);
[S029-Q11]
DecimalFormatで四捨五入するにはどうすればよいですか?
[S029-A11]
DecimalFormat df = new DecimalFormat();
df.setMaximumFractionDigits(3); //小数点以下の最大桁数
double x = Math.sin(30 * Math.PI / 180.0);
String str = df.format(x);
[S029-Q12]
四捨五入を実現するには?
[S029-A12]
DecimalFormat を使う他にも Math.round を活用して丸めることもできます。
ただし Math.roundは、0に近い方向に丸める為、
マイナスの値の場合は、思ったような値にはなりません。
また2進数浮動小数点を10進数表示するときに生じる誤差が発生します。
例えば小数点以下3桁表示したい場合、
x = Math.round(x * 1000.0) / 1000.0;
のようにできます。
ただし 2進数浮動小数点を10進数表示するときに生じる誤差があります。
場合にもよりますが、
java.text.DecimalFormat を使ったほうが簡単でしょう。
[S029-Q13]
バイナリファイルから double を読みこんだら値がおかしくなります。なぜでしょう?
DataInputStream.readDouble() で読んでいます。
[S029-A13]
エンディアネスの問題でしょう。
例えば Double.longBitsToDouble(readLong()); とした場合、
このreadLong()は big endian で読み出しを行っています。
読もうとしているファイルのフォーマットを調べてみてください。
このような場合バイトの順序をひっくり返して変換するとうまく行きます
いきなり readDouble() で読み込むのではなく
read() などでbyte配列に8バイト読み込んでから順序を
逆順にして long にし、その後で Double.longBitsToDouble()
により double の値に変換します。
例)
(in は InputStream またはそれを継承したクラスのインスタンス)
byte[] b = new byte[8];
in.read(b);
long n = 0L;
for (int i = 7; i >= 0; i--)
n = (n << 8) | (b[i] & 0xffL);
double d = Double.longBitsToDouble(n);
[S029-Q14]
バイト列から double を作るにはどうしたらよいでしょうか?
[S029-A14]
java.lang.Double#longBitsToDouble(long)
を使ってください。
例)
Double.longBitsToDouble(long bits) / Double.doubleToLongBits(double val)
[S029-Q15]
JDK 1.2 で追加された予約語「widefp」,「strictfp」とはなんですか?
[S029-A15]
public widefp class Matrix {
とか、
public strictfp double solve() {...}
というように使います。
「widefp宣言されたコード中では、IEEE 754 で規定された拡張フォーマットを
内部的に使ってもよいとする。strictfp宣言の場合は(計算結果の同一性
を保証するため)使ってはならない。」という決まりがあります。
ただし、widefp キーワードは、現在の最新の言語仕様では無くなっています。
デフォルトで widefp として扱われます。strictfpキーワードのみ宣言します。
[S029-Q16]
Double#toString()の結果のフォーマットはどう定義されているのでしょうか?
[S029-A16]
printf()系関数での%g変換と一緒であると思われます。
[S029-Q17]
IE 3.02/4.0 Win95/NTで、NaNが1.0になってしまうのはなぜでしょうか?
[S029-A17]
IE の JavaVM のバグです。
contributor: Yukio Andoh, mmura
コメントの送り先 Java FAQ BBS