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

发布于 2024-11-27 01:36:56 字数 696 浏览 4 评论 0原文

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

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

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

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

1308.75 -> "1308.7499998"

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

在识别有效数字方面,与 toString() 相比,DecimalFormat 似乎很糟糕,有什么办法可以修复它吗?我只想使用 toString() 风格处理有效数字,并将最大位数从 6 扩展到 7。

有什么想法吗? 谢谢

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.

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.

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.

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.

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.

Any ideas?
Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

逆流 2024-12-04 01:36:56

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

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

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

If you're concerned about preserving decimal values exactly, you should use BigDecimal to start with - double is fundamentally inappropriate, as a binary floating point type.

As it happens, I can't reproduce your behaviour of 1308.75 in DecimalFormat, which doesn't surprise me because that value is exactly representable as a double. In fact, DecimalFormat appears to be applying some heuristics anyway, as even 1308.76 comes out as 1308.76 - which surprises me as the actual value 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 double is concerned. If you need to distinguish between those two values, you should definitely be using 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.

调妓 2024-12-04 01:36:56

我觉得有点奇怪,所以我出去测试了一下。
我尝试了这段代码:

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 个 #),或者您的号码可能有尾随数字。要点是##.####### 和##.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));

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:

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));
八巷 2024-12-04 01:36:56

除了 Jon Skeet 提到的之外,为什么不保留一个 DecimalFormat 数组,每个数组包含多个数字?

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文