使用 String.format 将浮点值格式化为字符串的 Wierd 结果

Wierd result in formatting float value to string using String.format

提问人:dangling_refrenz 提问时间:9/1/2014 最后编辑:dangling_refrenz 更新时间:3/9/2015 访问量:787

问:

我是 Java 的新手。 我大致了解浮点精度和转换的问题,但不确定为什么在 Java 中看到如此荒谬的结果...... 我做到了......

String str1 = String.format("%.02f", 0.001921921f);
String str2 = String.format("%.02f", 9.0921921f);
String str3 = String.format("%.02f", 91.21921f);
String str4 = String.format("%.02f", 911212.09f); // WIERD: Prints 911212.06 !!!
String str5 = String.format("%.02f", 1212f); 

并在调试模式下的字符串中看到了这些值(Eclipse 调试器 / Eclipse Platform 4.2.1 / Java SE 6(Mac Mavericks OSX 默认))......

str1 = "0.00"
str2 = "9.09"
str3 = "91.22"
str4 = "911212.06"   ===> What the heck ?? Should be "911212.09" or some rounding of that?
str5 = "1212.00"

我完全不明白这一点。
Lemme 还解释了我最终想做什么......
我有一堆输入浮点值,其十进制精度各不相同,由“9.34”、“99.131”等字符串转换而来。
我想截断所有超过秒的小数位,并获得一个包含所有数字的 INT(没有小数位,没有下限/天花板/四舍五入等),即。19.2341 将成为 1923,19.2359 也将是 1923 年而不是 1924 年。
现在我试着做这样的事情

int ival = (int)(float_val * 100.0);

但这存在精度/准确性问题。例如。如果float_val包含 17.3F,则 IVAL 变为 1729。
我认为使用格式化字符串可能会有所帮助,但这也可以进行四舍五入,在尝试时,我还看到了上面的奇怪情况。
知道我做错了什么吗?
现在,对于我的情况,我将只操作原始字符串值来截断小数位并删除小数点,然后转换为 int。

Java 精度浮点 转换

评论

0赞 Kalpesh Soni 9/1/2014
尝试更新的 JDK,如果它的差异,那么你遇到了一个错误
0赞 jtahlborn 9/1/2014
如果需要精度,请使用 BigDecimal。
0赞 jtahlborn 9/1/2014
此外,将浮点数转换为 int 不会进行舍入,而是会截断。
0赞 jtahlborn 9/1/2014
我没有投反对票。在进行诽谤之前,您应该小心您所做的假设。

答:

5赞 ajb 9/1/2014 #1

A 具有 23 位精度。要表示 911212.09,在 + 或 - 0.005 的范围内,需要 27 位(我可能偏离 1)。因此,结果关闭也就不足为奇了。float

如果使用(通过从数字文本中删除 ),您将获得 52 位的精度,因此误差将足够小,以至于当您格式化为小数点后两位时,它不会影响结果。doublef

但是使用更好。BigDecimal

顺便说一句,这不是 Java 或 JDK 问题。它将出现在您尝试使用 32 位 IEEE 754 浮点类型的任何语言中。请阅读每个计算机科学家都应该了解的浮点运算知识。

5赞 ccjmne 9/1/2014 #2

您的问题只是 intern 表示(如 IEEE 754 格式中所述)不够精确,无法按预期进行处理。float911212.09f

它实际上只存储在一个 32 位的值上,分布如下:

  • 符号1
  • 指数为 8 位,并且只有...
  • 尾数23 位。

这给我们留下了 23 位的精度,而我们可以表示的最接近的 to 实际上是 。float911212.09911212.0625

您可以通过阅读此维基百科条目或使用此在线转换器来更深入地了解这一切是如何工作的: IEEE 754 转换器

enter image description here


正如@ajb在他的回答中提到的,只需使用类型就可以让你对这种情况进行排序;)double

即:

//                                        removed 'f' here
//                                                v
System.out.println(String.format("%.02f", 911212.09));

...将输出:

911212.09

评论

0赞 dangling_refrenz 9/1/2014
谢谢,这很清楚。在这种情况下使用双重工作