web-dev-qa-db-ja.com

倍精度で小数点以下を移動する

だから私は1234に等しい二重セットを持っているので、小数点以下を移動して12.34にしたい

これを行うには、.1から1234を2回乗算します。

double x = 1234;
for(int i=1;i<=2;i++)
{
  x = x*.1;
}
System.out.println(x);

これにより、結果「12.340000000000002」が出力されます

単純に小数点以下2桁にフォーマットせずに、二重に12.34を格納する方法はありますか?

94
BlackCow

doubleまたはfloatを使用する場合は、丸めを使用するか、丸めエラーが発生することが予想されます。これができない場合は、BigDecimalを使用します。

あなたが持っている問題は、0.1が正確な表現ではないことであり、計算を2回実行することにより、そのエラーを悪化させています。

ただし、100は正確に表すことができるため、次のことを試してください。

double x = 1234;
x /= 100;
System.out.println(x);

印刷するもの:

12.34

これは、Double.toString(d)がユーザーに代わって少量の丸めを実行するため機能しますが、それほど多くはありません。四捨五入せずにどのように見えるか疑問に思っている場合:

System.out.println(new BigDecimal(0.1));
System.out.println(new BigDecimal(x));

プリント:

0.100000000000000005551115123125782702118158340454101562
12.339999999999999857891452847979962825775146484375

要するに、浮動小数点での賢明な回答では、これを明示的に行っているかどうかにかかわらず、丸めは避けられません。


注:x / 100x * 0.01は、丸め誤差に関してはまったく同じではありません。これは、最初の式のラウンドエラーがxの値に依存するのに対し、2番目の式の0.01には固定ラウンドエラーがあるためです。

for(int i=0;i<200;i++) {
    double d1 = (double) i / 100;
    double d2 = i * 0.01;
    if (d1 != d2)
        System.out.println(d1 + " != "+d2);
}

プリント

0.35 != 0.35000000000000003
0.41 != 0.41000000000000003
0.47 != 0.47000000000000003
0.57 != 0.5700000000000001
0.69 != 0.6900000000000001
0.7 != 0.7000000000000001
0.82 != 0.8200000000000001
0.83 != 0.8300000000000001
0.94 != 0.9400000000000001
0.95 != 0.9500000000000001
1.13 != 1.1300000000000001
1.14 != 1.1400000000000001
1.15 != 1.1500000000000001
1.38 != 1.3800000000000001
1.39 != 1.3900000000000001
1.4 != 1.4000000000000001
1.63 != 1.6300000000000001
1.64 != 1.6400000000000001
1.65 != 1.6500000000000001
1.66 != 1.6600000000000001
1.88 != 1.8800000000000001
1.89 != 1.8900000000000001
1.9 != 1.9000000000000001
1.91 != 1.9100000000000001
185
Peter Lawrey

いいえ-10進値を正確に保存する場合は、BigDecimalを使用します。 double単純にできないは、0.1のような数を正確に表し、有限の10進数で3分の1の値を正確に書き込むことができます。

52
Jon Skeet

justフォーマットの場合、printfを試してください

double x = 1234;
for(int i=1;i<=2;i++)
{
  x = x*.1;
}
System.out.printf("%.2f",x);

出力

12.34
46
Augusto

金融ソフトウェアでは、ペニーに整数を使用するのが一般的です。学校では、浮動小数点ではなく固定小数点の使用方法を教えられましたが、通常は2のべき乗です。ペニーを整数で保存することは、「固定小数点」とも呼ばれます。

int i=1234;
printf("%d.%02d\r\n",i/100,i%100);

クラスでは、一般的に、どの数値を基数で正確に表現できるかを尋ねられました。

base=p1^n1*p2^n2...の場合、N = n * p1 ^ m1 * p2 ^ m2のNを表すことができます。

base=14=2^1*7^1...では、1/7 1/14 1/28 1/49を表現できますが、1/3は表現できません

金融ソフトウェアについて知っています-Ticketmasterの財務レポートをVAX asmからPascalに変換しました。彼らはペニーのためのコードを持つ独自のformatln()を持っていました。変換の理由は32ビット整数では不十分であったためです。 +/- 20億ペニーは2,000万ドルで、ワールドカップやオリンピックのためにあふれていた、私は忘れていた。

私は秘密を誓った。しかたがない。アカデミアでは、それが良ければ公開します。業界では、秘密にしておきます。

26

整数表現を試すことができます

int i =1234;
int q = i /100;
int r = i % 100;

System.out.printf("%d.%02d",q, r);
12
Angel Koh

これは、コンピューターが浮動小数点数を格納する方法が原因です。彼らは正確にはそうしません。プログラマーとして、 この浮動小数点ガイド を読んで、浮動小数点数の処理の試行錯誤に慣れてください。

10
CanSpice

多数の投稿がBigDecimalを使用するように言及しているが、BigDecimalに基づいて正しい答えを出すために誰も邪魔しないと言っているのは面白いですか?このコードで示されているように、BigDecimalを使用しても、まだ間違っている可能性があるためです。

String numstr = "1234";
System.out.println(new BigDecimal(numstr).movePointLeft(2));
System.out.println(new BigDecimal(numstr).multiply(new BigDecimal(0.01)));
System.out.println(new BigDecimal(numstr).multiply(new BigDecimal("0.01")));

この出力を与える

12.34
12.34000000000000025687785232264559454051777720451354980468750
12.34

BigDecimalコンストラクターは、数値コンストラクターよりもStringコンストラクターを使用する方が良いと特に述べています。究極の精度は、オプションのMathContextの影響も受けます。

BigDecimal Javadocによると、可能は、Stringコンストラクターを使用する場合、exactlyと等しいBigDecimalを作成します。 。

9
Justin Rowe

はいあります。各二重操作では精度が失われる可能性がありますが、精度は操作ごとに異なり、正しい操作シーケンスを選択することで最小化できます。たとえば、数値のセットを乗算する場合、乗算する前に指数でセットをソートするのが最善です。

数値計算に関するまともな本はこれを説明しています。例: http://docs.Oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

そしてあなたの質問に答えるために:

乗算の代わりに除算を使用すると、正しい結果が得られます。

double x = 1234;
for(int i=1;i<=2;i++)
{
  x =  x / 10.0;
}
System.out.println(x);
5
Jan Kotek

いいえ、 Java浮動小数点型 (実際にはすべての浮動小数点型)はサイズと精度のトレードオフです。多くのタスクに非常に役立ちますが、任意の精度が必要な場合は、BigDecimalを使用する必要があります。

3
biziclop