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做同样的计算。(由于其表示形式,使用双精度存在精度问题)。

  • 构造它new BigDecimal(String.valueOf(yourDouble))(这仍然是通过字符串,但部分不是通过字符串操作分隔)
  • 用于bd.subtract(new BigDecimal(bd.intValue())确定分数
于 2011-05-19T18:36:27.063 回答
5

这是基于BigDecimal(不经过 a 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在一个double!)这是因为数学实数 5.6 实际上不是用双精度表示的,而是 5.6 而是 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.valueOf,大多数 JDK(内部)通过调用来实现Double.toString。但至少与字符串相关的东西不会弄乱你的代码:-)


评论中有很好的后续问题:

如果它表示为 5.599999999...,那么为什么Double.toString(5.6)给出准确"5.6"

方法Double.toString其实很复杂。从以下文档Double.toString

[...]

m 或 a 的小数部分必须打印多少位?必须至少有一个数字来表示小数部分,除此之外,必须有尽可能多的数字,但只需要将参数值与相邻的 double 类型值唯一区分开来。也就是说,假设 x 是由该方法为有限非零参数 d 生成的十进制表示所表示的精确数学值。那么 d 必须是最接近 x 的 double 值;或者如果两个双精度值同样接近 x,则 d 必须是其中之一,并且 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。因为它具有较低的指数,归一化导致尾数向左移动三位。..66666..重复项 ( ) 最初是分数 7/5 的近似值这一事实已被遗忘,缺失的位被零替换。

将原始double值作为方法的输入,没有办法避免这种情况。要保留准确的值,您需要使用准确表示所需值的格式,例如Fraction来自 Apache commons-math。(对于这个带有d=5.6a 的特定示例BigDecimal也可以准确表示它,但还有其他数字不能准确表示,例如 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 回答