使用带有更多数字的 Java Double.toString() 样式格式(DecimalFormat 不适用于低精度数字)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6869144/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 17:39:43  来源:igfitidea点击:

Using Java Double.toString() style formatting with more digits (DecimalFormat does not work with low precision numbers)

javadoubleprecisiontostringdecimalformat

提问by user79126

The central problem is that I have a series of doubles that I need to log, each with varying number of significant digits. The numbers vary greatly in how many significant digits they have. Some have 0 (e.g. 5257), some have 2 (e.g. 1308.75), some have all the way up to 7 (e.g. 124.1171875). Basically everything between 0 to 7 significant digits after the decimal.

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

Standard Double.toString() works excellent on everything BUT those with 7 significant digits. That is all the way up to 6 digits, the significant digits are all printed without any insignificant digits. But on those with 7 significant digits, toString() rounds off the last digit. I.e.

标准 Double.toString() 对除 7 位有效数字以外的所有内容都表现出色。即一直到 6 位,有效数字全部打印出来,没有任何无效数字。但是对于那些有 7 位有效数字的人, toString() 会舍入最后一位数字。IE

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

Of course I tried using DecimalFormat("#.#######"), and that resolved the problem of missing significant digits, but it printed insignificant digits for many of the low precision doubles. I.e.

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

1308.75 -> "1308.7499998"

This is also unacceptable as 1) it wastes a significant amount of space (typically log >2 GB of data a day) and 2) it messes up the applications using the logs.

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

DecimalFormat seems to suck compared to toString() when it comes to identifying significant digits, is there anyway to fix it? I just want to use toString() style handling of significant digits, and extend the maximum number of digits from 6 to 7.

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

Any ideas? Thanks

有任何想法吗?谢谢

回答by Jon Skeet

If you're concerned about preserving decimal values exactly, you should use BigDecimalto start with - doubleis fundamentally inappropriate, as a binaryfloating point type.

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

As it happens, I can't reproduce your behaviour of 1308.75 in DecimalFormat, which doesn't surprise me because that value isexactly representable as a double. In fact, DecimalFormatappears to be applying some heuristics anyway, as even 1308.76 comes out as 1308.76 - which surprises me as the actualvalue is 1308.759999999999990905052982270717620849609375. It does mean that if you use 1308.7599999999999 in your code, it will come out as 1308.76, because it's the exact same value as far as doubleis concerned. If you need to distinguish between those two values, you should definitely be using BigDecimal.

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

Also note that 1308.75 has 6 significant digits- it has 2 decimal places. It's worth being careful to differentiate between the two concepts.

另请注意,1308.75 有 6 个有效数字- 它有 2个小数位。仔细区分这两个概念是值得的。

回答by n0rm1e

It seemed to me a bit strange, that's why I went out and tested. I tried this code:

我觉得有点奇怪,这就是我出去测试的原因。我试过这个代码:

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));
    }
}

And it gave me this result:

它给了我这个结果:

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

You probably were testing with "##.######" (only 6 #s after dot), or your number might have had trailing digits. The point is that ##.####### and ##.0000000 formats will round the last decimal point (e.g. 124.11718755 will be rounded to 124.1171876 before formatting). If you want it to be truncated try to truncate it first, doing something like this:

您可能正在使用“##.######”(点后仅 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));

回答by Tassos Bassoukos

Besides what Jon Skeet mentioned, why not keep an array of DecimalFormat, each one fo a number of digits?

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