格式化以尽可能高的精度进行加倍

Formatting doubles with highest possible precision

提问人:k314159 提问时间:6/30/2023 更新时间:7/4/2023 访问量:77

问:

像 0.1 这样的数字不能用二进制浮点数精确表示,所以我想以尽可能高的精度打印出来。我知道 64 位类型只能提供高达 15 位左右的有意义的精度,但更多的数字会揭示浮点近似值和实际值之间的差异。double

但是,具有高精度规范的格式(例如仍然没有给我):"%.50f"

String.format("%.50f", 0.1)    --> "0.10000000000000000000000000000000000000000000000000"
new BigDecimal(0.1).toString() --> "0.1000000000000000055511151231257827021181583404541015625"

有没有办法在不使用的情况下将数字输出为字符串?BigDecimal

Java 浮点 精度

评论

0赞 Jorn 6/30/2023
确定。只是原来的.toString()double
0赞 k314159 6/30/2023
@Jorn,这只给出“0.1”
3赞 chtz 6/30/2023
我不是Java专家,但我不明白为什么这不是一个有效的问题。例如,在 C/C++ 中打印出来(OP 期望的)——当然是 YMMV,因为 C 和 C++ 中的很多都是特定于编译器/目标的。输出 看起来非常错误(Java 只是在末尾附加 s 吗?printf("%.100g", 0.1)0.1000000000000000055511151231257827021181583404541015625"0.10000000000000000000000000000000000000000000000000"0
1赞 dan04 7/4/2023
BigDecimal在这里给出了正确的存储值 (0xCCCCCCCCCCCCD * 2^-55)。你有什么理由不想使用它吗?因为AFAICT,任何解决方案基本上都相当于重新发明了格式化程序。BigDecimal
3赞 k314159 7/4/2023
@LutzLehmann不是编译器很聪明,但如果你这样做并在控制台上输入 0.1,你仍然会得到相同的输出,这样编译器就不知道值会是什么,所以这一切都肯定是在运行时完成的。System.out.printf("%.30f", new Scanner(System.in).nextDouble());

答:

3赞 Joop Eggen 6/30/2023 #1

由于最后一个有意义的数字可以向上或向下舍入,因此不能只在它后面粘上 00...00 (99...99)。

因此,由于在大约 15 个奇数之后仍然存在近似误差,因此是合适的解决方案。您可以假设实际精度为 14 或更低,然后添加零。但仍然给出假新闻。new BigDecimal("...")printf/format

3赞 k314159 7/4/2023 #2

只是为了添加另一个更详细的答案:

“%f”格式专门记录在小数点后添加 0:

...可以附加零以达到精度。对于规范 表示值,请使用 Float.toString(float) 或 Double.toString(double) (视情况而定)。

Double.toString 记录为使用以下逻辑返回近似值:

m 或 a 的小数部分必须打印多少位数字? 必须至少有一位数字来表示小数部分,并且 除此之外,只要需要多少,但只能多一些数字 唯一区分参数值与相邻类型的值 双。

因此,为了获得二进制浮点值所代表的 String 表示形式,Java 似乎提供了 BigDecimal 作为唯一的选择。