web-dev-qa-db-ja.com

Javaのdoubleで精度を保持します

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だけで印刷(または使用)するにはどうすればよいですか?

131
Deinumite

他の人が述べたように、11.4の正確な表現が必要な場合は、おそらく BigDecimal クラスを使用する必要があります。

さて、これがなぜ起こっているのかについての少しの説明:

Javaのfloatおよびdoubleプリミティブ型は、 浮動小数点 数値です。この数値は、分数と指数のバイナリ表現として格納されます。

より具体的には、double型などの倍精度浮動小数点値は64ビット値です。ここで、

  • 1ビットは符号(正または負)を示します。
  • 指数用の11ビット。
  • 有効数字用の52ビット(2進数としての小数部)。

これらの部分を組み合わせて、値のdouble表現を生成します。

(出典: ウィキペディア:倍精度

Javaでの浮動小数点値の処理方法の詳細については、Java言語仕様の セクション4.2.3:浮動小数点のタイプ、フォーマット、および値 を参照してください。

bytecharintlongタイプは 固定小数点 数値であり、数値の正確な表現です。固定小数点数とは異なり、浮動小数点数は(「ほとんどの時間」を想定しても安全です)数値の正確な表現を返せない場合があります。これが、11.399999999999の結果として5.6 + 5.8になってしまう理由です。

1.5や150.1005などの正確な値が必要な場合は、数値を正確に表すことができる固定小数点型のいずれかを使用する必要があります。

すでに何度か言及したように、Javaには BigDecimal クラスがあり、非常に大きな数と非常に小さな数を処理します。

BigDecimalクラスのJava AP​​Iリファレンスから:

不変の任意精度の符号付き10進数。 BigDecimalは、スケールなしの任意精度整数値と32ビット整数スケールで構成されます。ゼロまたは正の場合、スケールは小数点の右側の桁数です。負の場合、数値のスケールなしの値に10のスケールの否定の乗を掛けます。したがって、BigDecimalで表される数値の値は(unscaledValue×10 ^ -scale)です。

Stack Overflowには、浮動小数点数とその精度に関する多くの質問があります。関連する質問のリストを次に示します。

浮動小数点数の細かい点に本当に行きたいのであれば、 すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと を見てください。

135
coobird

33.33333333333333などの倍数を入力すると、実際に得られる値は、最も近い表現可能な倍精度値であり、正確には次のとおりです。

33.3333333333333285963817615993320941925048828125

それを100で除算すると、次のようになります。

0.333333333333333285963817615993320941925048828125

倍精度の数値としても表現できないため、やはり最も近い表現可能な値に丸められます。

0.3333333333333332593184650249895639717578887939453125

この値を出力すると、まだ 17桁の10進数に丸められます。

0.33333333333333326
101
Stephen Canon

値を分数として処理するだけの場合は、分子と分母のフィールドを保持する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);
    }
}

}
23
Viral Shah

精度に制限のある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桁の精度を落とすだけで問題は解決すると思います。実験。

15
Steve Jessop

正確な数学が本当に必要な場合は、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
9
Vinny

7.3にはバイナリ形式の有限表現がないため、できません。最も近いものは2054767329987789/2 ** 48 = 7.3 + 1/1407374883553280です。

詳細については http://docs.python.org/tutorial/floatingpoint.html をご覧ください。 (Python Webサイトにありますが、JavaとC++には同じ「問題」があります。)

解決策は、問題の内容によって異なります。

  • ノイズの数字がすべて表示されない場合は、文字列の書式を修正してください。有効桁数が15桁(浮動小数点数の場合は7桁)を超えて表示しないでください。
  • 数値の不正確さが「if」ステートメントのようなものを壊している場合は、if(x == 7.3)ではなくif(abs(x-7.3)<TOLERANCE)を記述する必要があります。
  • お金で作業している場合、おそらく本当に必要なのは10進固定小数点です。整数のセントまたは通貨の最小単位を格納します。
  • (非常に珍しい)53有効ビット(15-16有効桁)を超える精度が必要な場合は、BigDecimalなどの高精度浮動小数点型を使用します。
5
dan04

Double型の精度の制限に直面しています。

Java.Mathには、任意精度の演算機能がいくつかあります。

5
John

他の人が指摘したように、10進数は10の累乗に基づいており、バイナリは2の累乗に基づいているため、すべての10進値がバイナリとして表現できるわけではありません。

精度が重要な場合はBigDecimalを使用しますが、わかりやすい出力が必要な場合:

System.out.printf("%.2f\n", total);

あなたに与えます:

11.40
5
Draemon

それを3行の例にできたことを十分に確認してください。 :)

正確な精度が必要な場合は、BigDecimalを使用してください。それ以外の場合、intに10 ^を掛けたものを使用できます。

5
Dustin
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
}
4
sravan

Java.math.BigDecimalを使用する

Doubleは内部的に2進小数であるため、小数を正確な小数に表すことができない場合があります。

3
Kevin Crowell

コンピューターは数値をバイナリで保存し、実際には33.333333333や100.0などの数値を正確に表すことはできません。これは、doubleの使用に関する厄介なことの1つです。ユーザーに表示する前に答えを丸める必要があります。幸いなことに、ほとんどのアプリケーションでは、それほど多くの小数点以下の桁数は必要ありません。

2
Jay Askren

浮動小数点数は、任意の浮動小数点数に対して次に高い浮動小数点数があるという点で、実数とは異なります。整数と同じです。 1〜2の整数はありません。

1/3をフロートとして表す方法はありません。その下にフロートがあり、その上にフロートがあり、それらの間には一定の距離があります。そして、1/3はそのスペースにあります。

Apfloat for Javaは、任意精度の浮動小数点数で動作すると主張していますが、私はそれを使用したことがありません。おそらく一見の価値があります。 http://www.apfloat.org/apfloat_Java/

同様の質問が前にここで尋ねられました Java浮動小数点高精度ライブラリ

2
Spike

すべてを100倍して、セント単位で保存します。

2
Paul Tomblin

ダブルは、Javaソースの10進数の概算です。 double(バイナリコード化された値)とソース(decimalコード化された)の不一致の結果がわかります。

Javaは最も近いバイナリ近似を生成します。 Java.text.DecimalFormatを使用して、見栄えの良い10進数値を表示できます。

1
S.Lott

BigDecimalを使用します。丸め規則を指定することもできます(ROUND_HALF_EVENなど、両方が同じ距離である場合に偶数隣に丸めることで統計誤差を最小限に抑えます;つまり、1.5と2.5の両方が2に丸められます)。

1
Adam Jaskiewicz

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;
}
0
user5734382

BigDecimalをチェックしてください。これは、そのような浮動小数点演算を扱う問題を処理します。

新しい呼び出しは次のようになります。

term[number].coefficient.add(co);

SetScale()を使用して、使用する小数点以下の桁数を設定します。

0
Dark Castle

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
0
Mr.Cat