小数点以下を2桁に移動する


97

だから私は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を正しく設定する方法はありますか?



43
あなたがしなかった理由はありますx /= 100;か?
Mark Ingram

回答:


189

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 / 100そしてx * 0.01、それは丸め誤差になるとまったく同じではありません。これは、最初の式の丸め誤差がxの値に依存するのに対し0.01、2番目の式の丸め誤差は固定の丸め誤差があるためです。

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

26
そもそもそんなことを考えていなかったなんて信じられない!ありがとう:-P
BlackCow

6
100はバイナリ形式で正確に表すことができますが、100による除算は正確に表すことができません。したがって、1234/100あなたがしたように、書くことは根本的な問題について実際には何もしません-それは書くこととまったく同じであるべき1234 * 0.01です。
Brooks Moses

1
@ピーター・ローリー:数値が奇数であるか、偶数であるかどうかが丸めに影響する理由をもっと説明できますか?/ = 100と* =。01は同じだと思います。100はintですが、型強制の結果としてとにかく100.0に変換されるからです。
eremzeit

1
/100および*0.01は互いに同等ですが、OPとは異なり*0.1*0.1ます。
アマダン2012年

1
私が言っているのは、0.1を2回乗算すると、平均して0.01を1回乗算するよりも大きな誤差が生じるということです。しかし、私は喜んで喜んで@JasperBekkersのポイントについて、100は異なり、正確にバイナリで表現できることを認めます。
アマダン

52

いいえ-10進値を正確に保存する場合は、を使用しますBigDecimaldouble単純にすることはできません任意のより多くのあなたが正確に小数点以下の桁数の有限数の3分の1の値を書き込むことができるよりも、正確に0.1のような数を表します。


46

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

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

出力

12.34

8
高い評価の回答は技術的に洞察に富んでいますが、これはOPの問題に対する正しい回答です。通常、double のわずかな不正確さは気にしないため、BigDecimalはやりすぎですが、表示するときには、出力が直感に一致することを確認したい場合が多いためSystem.out.printf()、正しい方法です。
dimo414

28

金融ソフトウェアでは、ペニーに整数を使用するのが一般的です。学校では、浮動小数点の代わりに固定小数点を使用する方法を学びましたが、それは通常2の累乗です。ペニーを整数で格納することも「固定小数点」と呼ばれる場合があります。

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

クラスでは、基本的に、ベースで正確に表すことができる数値を尋ねられました。

以下のためにbase=p1^n1*p2^n2...あなたは、任意のNここで、N = N * P1 ^ M1 * P2 ^平方メートルを表すことができます。

みましょうbase=14=2^1*7^1...あなたは1/7 1/14 1/28 1/49を表すことができますが1/3は表すことができません

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

私は秘密を誓った。しかたがない。academeaでは、それが良ければ公開します。業界では、あなたはそれを秘密にします。


12

あなたは整数表現を試すことができます

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

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

5
@ダン:なぜ?これは、ハードウェアレベルの速度を維持しながら、金融アプリ(または小さな丸め誤差でも許容できない他のすべてのアプリ)に対する正しいアプローチです。(もちろん、通常は毎回書き出されるわけではなく、クラスでラップされます)
アマダン

7
このソリューションにはわずかな問題があります。残りrが10未満の場合、0パディングは発生せず、1204は12.4の結果を生成します。正しいフォーマット文字列は "%d。%02d"に似ています
jakebman

10

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


ああ、私はちょうど同じ場所にリンクする説明を書いていた。+1。
Popは

@ロードハハ、ごめんなさい。とにかくスキートに乗った。:-)
CanSpice

それが理由だと思ったのですが、小数点以下を移動するための創造的な方法があるのでしょうか。12.34を倍精度できれいに格納することが可能であるため、0.1を掛けるのが気に入らない
BlackCow

1
12.34をdoubleにきれいに格納できたとしたら、Javaがそれを実行していたと思いませんか。そうではありません。他のデータ型(BigDecimalなど)を使用する必要があります。また、ループで計算するのではなく、100で除算してみませんか?
CanSpice、2011

ええと...そう、それを100で割るときれいな12.34になります...おかげで:-P
BlackCow

9

多数の投稿が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コンストラクターは、数値コンストラクターよりもストリングコンストラクターを使用するほうがよいと具体的に述べています。究極の精度は、オプションのMathContextにも影響されます。

BigDecimal Javadocによると、Stringコンストラクターを使用すると、正確に0.1 等しいBigDecimalを作成することができます。


5

はいあります。それぞれの二重操作で精度が失われる可能性がありますが、精度の量は操作ごとに異なり、正しい操作の順序を選択することで最小化できます。たとえば、一連の数値を乗算する場合は、乗算の前に指数でセットをソートするのが最適です。

数の計算に関するまともな本はこれを説明しています。例: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);

3

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.