5

核心问题是我需要记录一系列双打,每个双打都有不同数量的有效数字。这些数字的有效数字位数差异很大。有些有 0(例如 5257),有些有 2(例如 1308.75),有些一直到 7(例如 124.1171875)。基本上小数点后0到7位有效数字之间的所有内容。

标准 Double.toString() 对除 7 位有效数字之外的所有内容都非常有效。即一直到 6 位,有效数字全部打印,没有任何无效数字。但是对于具有 7 个有效数字的那些, toString() 将最后一位数字四舍五入。IE

5257 -> "5257"
1308.75 -> "1308.75"
124.1171875 -> "124.117188"

当然,我尝试使用 DecimalFormat("#.#######"),这解决了丢失有效数字的问题,但它打印了许多低精度双精度数的无效数字。IE

1308.75 -> "1308.7499998"

这也是不可接受的,因为 1) 它会浪费大量空间(通常每天记录 > 2 GB 的数据),以及 2) 它会弄乱使用日志的应用程序。

在识别有效数字方面,与 toString() 相比,DecimalFormat 似乎很糟糕,有没有办法解决它?我只想使用 toString() 样式处理有效数字,并将最大位数从 6 扩展到 7。

有任何想法吗?谢谢

4

3 回答 3

7

如果您担心精确保留十进制值,则应该使用BigDecimal-double作为二进制浮点类型从根本上不合适。

碰巧,我无法重现您的 1308.75 in 行为DecimalFormat,这并不让我感到惊讶,因为该值完全可以表示为double. 实际上,DecimalFormat无论如何,似乎都在应用一些启发式方法,因为即使 1308.76 也出现了 1308.76 - 这让我感到惊讶,因为实际值是 1308.759999999999990905052982270717620849609375。这确实意味着,如果您在代码中使用 1308.7599999999999,它将显示为 1308.76,因为就目前而言,它是完全相同的值double。如果您需要区分这两个值,则绝对应该使用BigDecimal.

还要注意 1308.75 有 6 个有效数字- 它有 2个小数位。区分这两个概念是值得小心的。

于 2011-07-29T06:00:30.410 回答
3

在我看来有点奇怪,这就是我出去测试的原因。我试过这段代码:

public class MySimpleTest
{
    public static void main(String args[])
    {
        format(5257);
        format(1308.75);
        format(124.1171875);
    }

    private static void format(double d)
    {
        DecimalFormat df = new DecimalFormat("##.#######");
        System.out.println("" + d + " -> " + df.format(d));
    }
}

它给了我这个结果:

5257.0 -> 5257
1308.75 -> 1308.75
124.1171875 -> 124.1171875

您可能正在使用“##.######”(点后仅 6 #s)进行测试,或者您的号码可能有尾随数字。关键是##.####### 和##.0000000 格式将四舍五入最后一个小数点(例如124.11718755 在格式化之前将四舍五入为124.1171876)。如果您希望它被截断,请先尝试截断它,执行以下操作:

double d = 124.1171875999999;
org.apache.commons.math.util.MathUtils.round(d, 6, java.math.BigDecimal.ROUND_DOWN);
DecimalFormat df = new DecimalFormat("##.#######");
System.out.println("" + d + " -> " + df.format(d));
于 2011-07-29T06:34:17.783 回答
-1

除了 Jon Skeet 提到的,为什么不保留一个 DecimalFormat 数组,每个数组都有多个数字?

于 2011-07-29T06:11:49.410 回答