12

次の方法で 5.6 (たとえば) を分離しようとしました。

private static double[] method(double d)
{
    int integerPart = 0;
    double fractionPart = 0.0;
    integerPart = (int) d;
    fractionPart = d - integerPart;
    return new double[]{integerPart, fractionPart};
}

しかし、私が得たものは次のとおりです。

[0] = 5.0
[1] = 0.5999999999999996

数値を文字列に変換せずにこれを行うことについて何か提案はありますか?

4

5 に答える 5

12

BigDecimal同じ計算を行うために使用します。(double を使用すると、その表現のために精度の問題があります)。

  • で構築しますnew BigDecimal(String.valueOf(yourDouble))(これはまだ文字列を通過していますが、文字列操作によってパーツが分離されていません)
  • bd.subtract(new BigDecimal(bd.intValue())分数を決定するために使用します
于 2011-05-19T18:36:27.063 に答える
5

BigDecimal(を経由しない)に基づく別のソリューションを次に示しStringます。

private static double[] method(double d) {
    BigDecimal bd = new BigDecimal(d);
    return new double[] { bd.intValue(),
                          bd.remainder(BigDecimal.ONE).doubleValue() };
}

0.6お気づきのように、小数部分の出力はまだ得られません。0.6( a に格納することさえできませんdouble!) これは、数学的な実数 5.6 が、実際には 5.6 とまったく同じ double ではなく、5.599999 として表されるという事実によるものです...


あなたもできる

private static double[] method(double d) {
    BigDecimal bd = BigDecimal.valueOf(d);
    return new double[] { bd.intValue(),
                          bd.remainder(BigDecimal.ONE).doubleValue() };
}

これは実際に利回りを行い[5.0, 0.6]ます。

ただし、ほとんどのBigDecimal.valueOfJDK では (内部的に) への呼び出しによって実装されていますDouble.toString。しかし、少なくとも文字列関連のものでコードが乱雑なることはありません:-)


コメントでの良いフォローアップの質問:

5.599999999... として表される場合、なぜDouble.toString(5.6)正確に"5.6"

このDouble.toString方法は実際には非常に洗練されています。のドキュメントからDouble.toString:

[...]

m または a の小数部分は何桁で表示する必要がありますか? 小数部分を表すには少なくとも 1 桁が必要であり、それを超える桁数は、引数の値を double 型の隣接する値と一意に区別するために必要な数だけです。つまり、x が、有限の非ゼロ引数 d に対してこのメ​​ソッドによって生成された 10 進数表現によって表される正確な数学的値であるとします。この場合、d は x に最も近い double 値でなければなりません。または、2 つの double 値が x に等しく近い場合、d はそれらの 1 つでなければならず、d の仮数の最下位ビットは 0 でなければなりません。

[...]

"5.6"文字を取得するコードは次のようになりFloatingDecimal.getCharsます。

private int getChars(char[] result) {
    assert nDigits <= 19 : nDigits; // generous bound on size of nDigits
    int i = 0;
    if (isNegative) { result[0] = '-'; i = 1; }
    if (isExceptional) {
        System.arraycopy(digits, 0, result, i, nDigits);
        i += nDigits;
    } else {
        if (decExponent > 0 && decExponent < 8) {
            // print digits.digits.
            int charLength = Math.min(nDigits, decExponent);
            System.arraycopy(digits, 0, result, i, charLength);
            i += charLength;
            if (charLength < decExponent) {
                charLength = decExponent-charLength;
                System.arraycopy(zero, 0, result, i, charLength);
                i += charLength;
                result[i++] = '.';
                result[i++] = '0';
            } else {
                result[i++] = '.';
                if (charLength < nDigits) {
                    int t = nDigits - charLength;
                    System.arraycopy(digits, charLength, result, i, t);
                    i += t;
                } else {
                    result[i++] = '0';
                }
            }
        } else if (decExponent <=0 && decExponent > -3) {
            result[i++] = '0';
            result[i++] = '.';
            if (decExponent != 0) {
                System.arraycopy(zero, 0, result, i, -decExponent);
                i -= decExponent;
            }
            System.arraycopy(digits, 0, result, i, nDigits);
            i += nDigits;
        } else {
            result[i++] = digits[0];
            result[i++] = '.';
            if (nDigits > 1) {
                System.arraycopy(digits, 1, result, i, nDigits-1);
                i += nDigits-1;
            } else {
                result[i++] = '0';
            }
            result[i++] = 'E';
            int e;
            if (decExponent <= 0) {
                result[i++] = '-';
                e = -decExponent+1;
            } else {
                e = decExponent-1;
            }
            // decExponent has 1, 2, or 3, digits
            if (e <= 9) {
                result[i++] = (char)(e+'0');
            } else if (e <= 99) {
                result[i++] = (char)(e/10 +'0');
                result[i++] = (char)(e%10 + '0');
            } else {
                result[i++] = (char)(e/100+'0');
                e %= 100;
                result[i++] = (char)(e/10+'0');
                result[i++] = (char)(e%10 + '0');
            }
        }
    }
    return i;
}
于 2011-05-19T18:42:30.743 に答える
1

何が起こっているかを確認するには、数値のバイナリ表現を見てください。

double d = 5.6;
System.err.printf("%016x%n", Double.doubleToLongBits(d));
double[] parts = method(d);
System.err.printf("%016x %016x%n",
                  Double.doubleToLongBits(parts[0]),
                  Double.doubleToLongBits(parts[1]));

出力:

4016666666666666
4014000000000000 3fe3333333333330

5.6 は 1.4 * 2 2ですが、0.6 は 1.2 * 2 -1です。指数が小さいため、正規化により仮数が 3 ビット左にシフトされます。反復項 ( ..66666..) がもともと分数 7/5 の近似値であったという事実は忘れられており、欠落しているビットはゼロに置き換えられています。

メソッドへの入力として元のdouble値を指定すると、これを回避する方法はありません。Fraction正確な値を保持するには、Apache commons-math などから、目的の値を正確に表す形式を使用する必要があります。(この特定の例ではd=5.6、 aBigDecimalも正確に表すことができますが、正確に表すことができない他の数があります。たとえば、4/3)

于 2011-05-19T19:20:36.093 に答える
0

貧乏人の解決策(文字列を使用)

    static double[] sp(double d) {
        String str = String.format(Locale.US, "%f", d);
        int i = str.indexOf('.');
        return new double[] {
            Double.parseDouble(str.substring(0, i)),
            Double.parseDouble(str.substring(i))
        };
    }

(ロケールなので、実際に小数点を取得します)

于 2011-05-19T19:22:17.380 に答える
-1

String doubleAsString = Double.toString(123.456);

String beforeDecimal=doubleAsString.substring(0,doubleAsString.indexOf(".")); //123

String afterDecimal=doubleAsString.substring(doubleAsString.indexOf(".")+1); //456

于 2014-10-22T06:46:31.940 に答える