public class doublePrecision {
public static void main(String[] args) {
double total = 0;
total += 5.6;
total += 5.8;
System.out.println(total);
}
}
上記のコードは次を印刷します:
11.399999999999
これを11.4だけで印刷(または使用)するにはどうすればよいですか?
他の人が述べたように、11.4の正確な表現が必要な場合は、おそらく BigDecimal
クラスを使用する必要があります。
さて、これがなぜ起こっているのかについての少しの説明:
Javaのfloat
およびdouble
プリミティブ型は、 浮動小数点 数値です。この数値は、分数と指数のバイナリ表現として格納されます。
より具体的には、double
型などの倍精度浮動小数点値は64ビット値です。ここで、
これらの部分を組み合わせて、値のdouble
表現を生成します。
(出典: ウィキペディア:倍精度 )
Javaでの浮動小数点値の処理方法の詳細については、Java言語仕様の セクション4.2.3:浮動小数点のタイプ、フォーマット、および値 を参照してください。
byte
、char
、int
、long
タイプは 固定小数点 数値であり、数値の正確な表現です。固定小数点数とは異なり、浮動小数点数は(「ほとんどの時間」を想定しても安全です)数値の正確な表現を返せない場合があります。これが、11.399999999999
の結果として5.6 + 5.8
になってしまう理由です。
1.5や150.1005などの正確な値が必要な場合は、数値を正確に表すことができる固定小数点型のいずれかを使用する必要があります。
すでに何度か言及したように、Javaには BigDecimal
クラスがあり、非常に大きな数と非常に小さな数を処理します。
BigDecimal
クラスのJava APIリファレンスから:
不変の任意精度の符号付き10進数。 BigDecimalは、スケールなしの任意精度整数値と32ビット整数スケールで構成されます。ゼロまたは正の場合、スケールは小数点の右側の桁数です。負の場合、数値のスケールなしの値に10のスケールの否定の乗を掛けます。したがって、BigDecimalで表される数値の値は(unscaledValue×10 ^ -scale)です。
Stack Overflowには、浮動小数点数とその精度に関する多くの質問があります。関連する質問のリストを次に示します。
浮動小数点数の細かい点に本当に行きたいのであれば、 すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと を見てください。
33.33333333333333
などの倍数を入力すると、実際に得られる値は、最も近い表現可能な倍精度値であり、正確には次のとおりです。
33.3333333333333285963817615993320941925048828125
それを100で除算すると、次のようになります。
0.333333333333333285963817615993320941925048828125
倍精度の数値としても表現できないため、やはり最も近い表現可能な値に丸められます。
0.3333333333333332593184650249895639717578887939453125
この値を出力すると、まだ 17桁の10進数に丸められます。
0.33333333333333326
値を分数として処理するだけの場合は、分子と分母のフィールドを保持するFractionクラスを作成できます。
ToDoubleメソッドだけでなく、加算、減算、乗算、除算のメソッドを記述します。これにより、計算中にフロートを回避できます。
編集:迅速な実装、
public class Fraction {
private int numerator;
private int denominator;
public Fraction(int n, int d){
numerator = n;
denominator = d;
}
public double toDouble(){
return ((double)numerator)/((double)denominator);
}
public static Fraction add(Fraction a, Fraction b){
if(a.denominator != b.denominator){
double aTop = b.denominator * a.numerator;
double bTop = a.denominator * b.numerator;
return new Fraction(aTop + bTop, a.denominator * b.denominator);
}
else{
return new Fraction(a.numerator + b.numerator, a.denominator);
}
}
public static Fraction divide(Fraction a, Fraction b){
return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator);
}
public static Fraction multiply(Fraction a, Fraction b){
return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator);
}
public static Fraction subtract(Fraction a, Fraction b){
if(a.denominator != b.denominator){
double aTop = b.denominator * a.numerator;
double bTop = a.denominator * b.numerator;
return new Fraction(aTop-bTop, a.denominator*b.denominator);
}
else{
return new Fraction(a.numerator - b.numerator, a.denominator);
}
}
}
精度に制限のある10進数演算を使用し、1/3を処理したい場合も同じ問題があることに注意してください。0.333333333* 3は1.00000000ではなく0.999999999です。
残念なことに、5.6、5.8、および11.4は5進数を含むため、2進数の丸め数値ではありません。したがって、0.3333が正確に1/3ではないように、それらのfloat表現は正確ではありません。
使用するすべての数値が非小数であり、正確な結果が必要な場合は、BigDecimalを使用します。または、他の人が言ったように、値がすべて0.01または0.001などの倍数であるという意味でお金のようなものである場合、すべてを10の固定累乗で乗算し、intまたはlongを使用します(加算と減算はつまらない:乗算に注意してください)。
ただし、バイナリの計算に満足しているが、少し使いやすい形式で印刷したい場合は、Java.util.Formatter
またはString.format
を試してください。書式文字列では、doubleの完全精度よりも低い精度を指定します。有効数字10桁まで、たとえば11.399999999999は11.4であるため、バイナリの結果が小数点以下数桁のみを必要とする値に非常に近い場合、結果はほぼ正確で人間が読めるようになります。
指定する精度は、数値を使用して計算した量に少し依存します-一般に、実行するほどエラーが累積しますが、一部のアルゴリズムは他のアルゴリズムよりもはるかに速く累積します(「不安定」と呼ばれます)丸め誤差に関して「安定」ではありません)。いくつかの値を追加するだけなら、小数点以下1桁の精度を落とすだけで問題は解決すると思います。実験。
正確な数学が本当に必要な場合は、JavaのJava.math.BigDecimalクラスの使用を検討してください。 BigDecimalの場合 に関するOracle/Sunの優れた記事を次に示します。誰かが言及したように1/3を表すことはできませんが、()結果の精度を正確に決定することができます。 setScale()はあなたの友人です.. :)
わかりました。現時点では、あなたの質問に関連するコード例がここにあります。
import Java.math.BigDecimal;
/**
* Created by a wonderful programmer known as:
* Vincent Stoessel
* [email protected]
* on Mar 17, 2010 at 11:05:16 PM
*/
public class BigUp {
public static void main(String[] args) {
BigDecimal first, second, result ;
first = new BigDecimal("33.33333333333333") ;
second = new BigDecimal("100") ;
result = first.divide(second);
System.out.println("result is " + result);
//will print : result is 0.3333333333333333
}
}
そして、私の新しいお気に入りの言語であるGroovyをプラグインするために、同じもののより良い例があります:
import Java.math.BigDecimal
def first = new BigDecimal("33.33333333333333")
def second = new BigDecimal("100")
println "result is " + first/second // will print: result is 0.33333333333333
7.3にはバイナリ形式の有限表現がないため、できません。最も近いものは2054767329987789/2 ** 48 = 7.3 + 1/1407374883553280です。
詳細については http://docs.python.org/tutorial/floatingpoint.html をご覧ください。 (Python Webサイトにありますが、JavaとC++には同じ「問題」があります。)
解決策は、問題の内容によって異なります。
Double型の精度の制限に直面しています。
Java.Mathには、任意精度の演算機能がいくつかあります。
他の人が指摘したように、10進数は10の累乗に基づいており、バイナリは2の累乗に基づいているため、すべての10進値がバイナリとして表現できるわけではありません。
精度が重要な場合はBigDecimalを使用しますが、わかりやすい出力が必要な場合:
System.out.printf("%.2f\n", total);
あなたに与えます:
11.40
それを3行の例にできたことを十分に確認してください。 :)
正確な精度が必要な場合は、BigDecimalを使用してください。それ以外の場合、intに10 ^を掛けたものを使用できます。
private void getRound() {
// this is very simple and interesting
double a = 5, b = 3, c;
c = a / b;
System.out.println(" round val is " + c);
// round val is : 1.6666666666666667
// if you want to only two precision point with double we
// can use formate option in String
// which takes 2 parameters one is formte specifier which
// shows dicimal places another double value
String s = String.format("%.2f", c);
double val = Double.parseDouble(s);
System.out.println(" val is :" + val);
// now out put will be : val is :1.67
}
Java.math.BigDecimalを使用する
Doubleは内部的に2進小数であるため、小数を正確な小数に表すことができない場合があります。
コンピューターは数値をバイナリで保存し、実際には33.333333333や100.0などの数値を正確に表すことはできません。これは、doubleの使用に関する厄介なことの1つです。ユーザーに表示する前に答えを丸める必要があります。幸いなことに、ほとんどのアプリケーションでは、それほど多くの小数点以下の桁数は必要ありません。
浮動小数点数は、任意の浮動小数点数に対して次に高い浮動小数点数があるという点で、実数とは異なります。整数と同じです。 1〜2の整数はありません。
1/3をフロートとして表す方法はありません。その下にフロートがあり、その上にフロートがあり、それらの間には一定の距離があります。そして、1/3はそのスペースにあります。
Apfloat for Javaは、任意精度の浮動小数点数で動作すると主張していますが、私はそれを使用したことがありません。おそらく一見の価値があります。 http://www.apfloat.org/apfloat_Java/
同様の質問が前にここで尋ねられました Java浮動小数点高精度ライブラリ
すべてを100倍して、セント単位で保存します。
ダブルは、Javaソースの10進数の概算です。 double(バイナリコード化された値)とソース(decimalコード化された)の不一致の結果がわかります。
Javaは最も近いバイナリ近似を生成します。 Java.text.DecimalFormatを使用して、見栄えの良い10進数値を表示できます。
BigDecimalを使用します。丸め規則を指定することもできます(ROUND_HALF_EVENなど、両方が同じ距離である場合に偶数隣に丸めることで統計誤差を最小限に抑えます;つまり、1.5と2.5の両方が2に丸められます)。
Double値を使用する以外に選択肢がない場合は、以下のコードを使用できます。
public static double sumDouble(double value1, double value2) {
double sum = 0.0;
String value1Str = Double.toString(value1);
int decimalIndex = value1Str.indexOf(".");
int value1Precision = 0;
if (decimalIndex != -1) {
value1Precision = (value1Str.length() - 1) - decimalIndex;
}
String value2Str = Double.toString(value2);
decimalIndex = value2Str.indexOf(".");
int value2Precision = 0;
if (decimalIndex != -1) {
value2Precision = (value2Str.length() - 1) - decimalIndex;
}
int maxPrecision = value1Precision > value2Precision ? value1Precision : value2Precision;
sum = value1 + value2;
String s = String.format("%." + maxPrecision + "f", sum);
sum = Double.parseDouble(s);
return sum;
}
BigDecimalをチェックしてください。これは、そのような浮動小数点演算を扱う問題を処理します。
新しい呼び出しは次のようになります。
term[number].coefficient.add(co);
SetScale()を使用して、使用する小数点以下の桁数を設定します。
Mathクラスのround()メソッドを使用しないのはなぜですか?
// The number of 0s determines how many digits you want after the floating point
// (here one digit)
total = (double)Math.round(total * 10) / 10;
System.out.println(total); // prints 11.4