我最近刚接触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。
答案 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位值上,分布如下:
这使得我们只有 23 位的精度,而我们可以表示为float
到911212.09
的最接近的位数实际为911212.0625
。
通过阅读this Wikipedia entry或使用此在线转换器,您可以更深入地了解这一切是如何工作的:IEEE 754 Converter
正如his answer中提到的@ajb一样,只需使用double
类型就可以根据这种情况进行排序;)
即:
// removed 'f' here
// v
System.out.println(String.format("%.02f", 911212.09));
...将输出:
911212.09