使用 DecimalFormat 格式化 Double 以在没有科学计数法的情况下打印

Posted

技术标签:

【中文标题】使用 DecimalFormat 格式化 Double 以在没有科学计数法的情况下打印【英文标题】:Formatting Double to print without scientific notation using DecimalFormat 【发布时间】:2012-11-30 07:55:57 【问题描述】:

我一直在从传感器读数中读取时间戳值,但由于它们是以纳秒为单位提供的,我想我会将它们转换为加倍并进行转换。结果数字是一个 17 位的值,加上分隔符。

尝试直接打印它会导致科学记数法,这是我不想要的,因此我使用 DecimalFormat 类将其输出到小数点后 4 位的预期值。问题是,即使调试器显示了 17 位十进制数字,即使在“doubleValue()”调用之后,输出字符串也显示了 15 位数字。

代码:

...
Double timestamp = (new Date().getTime()) +       // Example: 1.3552299670232847E12
            ((event.timestamp - System.nanoTime()) / 1000000D);
DecimalFormat dfmt = new DecimalFormat("#.####");

switch(event.sensor.getType())
    case Sensor.TYPE_LINEAR_ACCELERATION:
    case Sensor.TYPE_ACCELEROMETER:
        accel = event.values.clone();
        String line = "A" + LOGSEPARATOR +              
            dfmt.format(timestamp.doubleValue()) + // Prints: 1355229967023.28
...

我认为这可能是一个 android 精度问题,但调试器的格式化程序也显示了错误的精度。我在本地 java 程序中对此进行了测试,两个调用的位数相同。

这是 DecimalFormat 错误/限制吗?还是我做错了什么?

【问题讨论】:

您的问题是数字不符合格式。试试“##############.####”。 当时我也试过了,但还是检查了一下,以防我忘记了。格式“###################.####”(有 7 个额外数字)得到相同的结果。 您使用的是 java.util 版本的 DecimalFormat 还是 Android 版本? 我不认为Android有一个DecimalFormat类,因为documentation上唯一的一个来自java。 我只是想问一下——Android 有自己的几个类版本,这可能会造成混乱。 【参考方案1】:

Java 中的 double 的尾数只有 52 位(将隐藏的 1 计算在内为 53 位)。这相当于 15-16 位小数 (53*log10(2))。这之后的每个数字都是随机的,因此转换函数将输出减少到小数点后 15 位是有意义的。

既然你不需要 double 提供的大数字范围,为什么不保持这个值一样长呢?这将为您提供 63 个有效位(符号为 64 -1)。

【讨论】:

这是我的推理:我需要将纳秒转换为毫秒,用两个 long 计算。但我没想到反过来:将毫秒更改为纳秒,这不需要浮点数据。这样我就可以保持长整数的精度,并且在打印出来时只需将逗号向左移动 6 位。让我测试一下,我会回复你的。 成功了!我不仅获得了其他两个名额,而且还获得了额外的 2 个名额。奇怪的是,我原以为用前一种方法计算的最后两个字符只是随机数,但它们与我新计算的 long 值中的字符匹配。 我将授予您赏金并添加我自己的格式化答案。不过,我觉得有点愚蠢,因为我想过做类似的事情,但没有打扰/认为这是狡猾的。 我现在无法奖励赏金,但如果我忘记了,请随时提醒我。 ;) 差点忘了给你颁奖。享受你的代表!【参考方案2】:

Java 和 Android 的 DecimalFormat 类之间确实存在差异,尽管采用完全相同的参数,但它们输出的结果也不同。

这足以让我尝试亨利的方法,现在我发现我已经获得了额外的 2 位精度。我也相信这些值是准确计算的,因为只涉及求和和乘法。

这是我最终使用的修改后的代码:

...
long javaTime = new Date().getTime();
long nanoTime = System.nanoTime();
long newtimestamp = javaTime * 1000000 +            // Compute the timestamp
            (event.timestamp - nanoTime);           // in nanos first
String longStr = Long.valueOf(newtimestamp).toString();
String tsString = longStr.substring(0, longStr.length()-6) +// Format the output string
            "." + longStr.substring(longStr.length()-6);    // to have the comma in the
                                                            // correct space.
...

【讨论】:

【参考方案3】:

正在对 String.format 进行一些研究,结果相同。

Double timestamp = 1.3552299670232847E12;
System.out.println("it was " + timestamp);
System.out.println("and now " + String.format("%.4f", timestamp));

这是输出:

12-12 15:48:58.255: I/System.out(2989): it was 1.3552299670232847E12
12-12 15:48:58.255: I/System.out(2989): and now 1355229967023,2800

也许你是对的,这是一个Android精度问题,就像你在Java中尝试一样,输出是正确的:http://ideone.com/PBOiet

我会继续搜索...

【讨论】:

以上是关于使用 DecimalFormat 格式化 Double 以在没有科学计数法的情况下打印的主要内容,如果未能解决你的问题,请参考以下文章

java 使用DecimalFormat进行数字的格式化实例详解

DecimalFormat类

DecimalFormat 格式化金额

关于DecimalFormat的用法

DecimalFormat - 没有小数分隔符

Java数据格式化使用DecimalFormat 对Float和double进行格式化