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

时间:2014-09-01 01:27:39

标签: java floating-point floating-accuracy floating-point-conversion

我最近刚接触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平台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。

2 个答案:

答案 0 :(得分:5)

float有23位精度。要表示911212.09,在+或 - 0.005的范围内,需要27位(我可以关闭一个)。因此,结果不应该是令人惊讶的。

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

但使用BigDecimal会更好。

顺便说一下,这不是Java或JDK问题。它将出现在您尝试使用32位IEEE 754浮点类型的任何语言中。请阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic

答案 1 :(得分:5)

您的问题很简单,float的实习生表示(如 IEEE 754 格式所述)不够精确,无法按预期处理911212.09f。< / p>

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

    1 位 指数的
  • 8 位且仅...
  • 尾数
  • 23 位。

这使得我们只有 23 位的精度,而我们可以表示为float911212.09的最接近的位数实际为911212.0625

通过阅读this Wikipedia entry或使用此在线转换器,您可以更深入地了解这一切是如何工作的:IEEE 754 Converter

enter image description here


正如his answer中提到的@ajb一样,只需使用double类型就可以根据这种情况进行排序;)

即:

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

...将输出:

911212.09