我对Java NumberFormat
的行为感到困惑。
考虑以下方法,将double值转换为其百分比表示形式,并将结果百分比舍入为三位小数:
public static String doubleToPercent(final double val) {
NumberFormat nf = NumberFormat.getPercentInstance();
// Default rounding mode is HALF_EVEN
nf.setRoundingMode(RoundingMode.HALF_UP);
nf.setMaximumFractionDigits(3);
return nf.format(val);
}
舍入模式为HALF_UP
。
worksAsExpected
的结果并不让我感到惊讶;测试通过:
@Test
public void worksAsExpected() {
double input = 1.234585;
String expected = "123.459%";
String output = doubleToPercent(input);
assertEquals(expected, output);
}
但是这个怎么样:
@Test
public void surprise() {
double input = 1.234535;
String expected = "123.454%";
String output = doubleToPercent(input);
assertEquals(expected, output);
}
为什么这个测试失败了?为什么1.234535
向上舍入而1.234585
被舍入?
另一方面,如果最后一个数字是6
,则该数字将向上舍入。
@Test
public void noSurprise() {
double input = 1.234536;
String expected = "123.454%";
String output = doubleToPercent(input);
assertEquals(expected, output);
}
这与double
精度的限制有关吗?我想像1.234535
这样的数字在double
的能力范围内。
我正在使用运行Windows 7 x86的Java 1.7.0_51。
感谢任何见解。
答案 0 :(得分:2)
1.234535 = 1.2345349999999999379696191681432537734508514404296875为double,因此对于6位数,它向下舍入为1.23453。
您可以使用我的十进制/二进制转换器exploringbinary.com/binary-converter来查看会发生什么。
注意:与普通二进制数不同,二进制小数的精确十进制表示始终具有相同的位数:
0.001 =
0.125,
0.1101101 =
0.8515625,
0.00001110101011 =
0.05731201171875,
1.0011110000001010011111000101101011000100011100011011 =
1.2345349999999999379696191681432537734508514404296875
etc.
如果您需要更多控制小数位和舍入,则必须使用BigDecimal。请注意,计算对数字执行的每个操作的舍入误差最好。 See this question for details
答案 1 :(得分:0)
使用双精度浮点格式(http://en.wikipedia.org/wiki/Double-precision_floating-point_format)存储数字。
我很惊讶您遇到了精度方面的问题,遗憾的是,double
在Java中并不是一种执行精确计算的非常可靠的方法。
这也是其中一个原因,因为对于数学模块,其他工具如Matlab是首选。
无论如何,在你的例子中,你可以解决你的问题(希望)增加nf.setMaximumFractionDigits(<something bigger than 3>);
中数字的数字
或者,您可以根据需要切换到BigDecimal
或DecimalFormat
。