1

高精度の整数 (long または BigInteger) を小さな倍精度浮動小数点数 (> 0 および < 1 について考える) で乗算し、演算の正確な算術値の算術丸め整数 (long または BigInteger) を結果として取得したいと考えています。

double から integer への変換は機能しません。これは、小数値が失われるためです。

整数を double に変換してから、乗算して結果を整数に戻すこともできません。これは、double の精度が十分でないためです。もちろん、二重オペランドはそもそも十分に正確ではないため、結果が同じ桁数で正確でないことは問題にならないかもしれないと主張することもできますが、この場合はそうです。

おまけの質問:

BigDecimal の使用は機能しますが、非常に効率が悪いようです。long を double に変換してから乗算し、元に戻すと、BigDecimal に変換するよりも 500 倍高速に実行されるようです (ただし、精度は失われます)。より効率的な可能性はありますか?いくつかの異なる long をそれぞれ同じ double で乗算すると、パフォーマンスを向上させることは可能ですか?

4

4 に答える 4

2

精度を維持するためにBigDecimalを使用します。

BigInteger myBI = new BigInteger("99999999999999999");
Double d = 0.123;
BigDecimal bd = new BigDecimal(myBI);
BigDecimal result = bd.multiply(BigDecimal.valueOf(d));
于 2013-05-14T13:05:58.107 に答える
1

BigDecimal を使用すると実際に機能します。double が表す正確な値を使用し、算術的に丸めることに注意する必要があります。

    BigInteger myBI = new BigInteger("1000000000000000000000000000000000000000000000000000000");
    double d = 0.1;
    BigDecimal bd = new BigDecimal(myBI);

    BigInteger doubleWithStringValue = bd.multiply(BigDecimal.valueOf(d)).toBigInteger();

    BigDecimal bdresult = bd.multiply(new BigDecimal(d));
    BigInteger unrounded = bdresult.toBigInteger();
    BigInteger correct = bdresult.add(new BigDecimal("0.5")).toBigInteger(); // this way of rounding assumes positive numbers
    BigInteger lostprecision = new BigDecimal(myBI.doubleValue() * d).toBigInteger();

    System.out.println("DoubleString:   " + doubleWithStringValue);

    System.out.println("Unrounded:      " + unrounded);
    System.out.println("Correct:        " + correct);
    System.out.println("Lost precision: " + lostprecision);

出力:

DoubleString:   100000000000000000000000000000000000000000000000000000
Unrounded:      100000000000000005551115123125782702118158340454101562
Correct:        100000000000000005551115123125782702118158340454101563
Lost precision: 100000000000000020589742799994816764107083808679919616
于 2013-05-15T07:54:57.233 に答える
0

Double の精度は 52 ビットです。どうですか:

  1. あなたのダブルを(1<<52)で掛けます
  2. double を BigInteger に変換します (完全な精度は小数点の左側にあるため、損失はありません)
  3. 他の BigIngeger と掛け合わせる
  4. 結果の部分的に正しい 2 進指数 (BigInteger>>51)
  5. 奇数の場合は、1 または BigInteger.Sign を追加して丸めを行います (丸めの設定に応じて)
  6. 最後に結果をもう 1 ビットシフトします (BigInteger>>1)

    BigInteger myBI = BigInteger("99999999999999999");
    ダブル d = 0.123;
    BigInteger bigDouble = (BigInteger)(d * ((ulong)1 << 52));
    BigInteger 結果 = (myBI * bigDouble) >> 51; if (!result.IsEven)
    結果 += 結果.Sign; 結果 = 結果 >> 1;

于 2015-03-18T23:33:14.537 に答える
0

私が見ることができる最善の解決策は、Math.round 関数を使用することです。このようなコードで。

long l; //your long value
double d;//your fraction
long answer;

answer = Math.round((double)(l * d));

これにより、紛失防止エラーなしで答えが得られます。他のオプションは、それを切り捨てることです。

上記のコードと同じ宣言。

String s;

s = "" + (l*d);
StringTokenizer token = new StringTokenizer(s);
s = token.nextToken();
answer = Long(s);
于 2013-05-14T12:57:30.257 に答える