2011-12-28 99 views
3

基本上,我不明白为什么当4.35 * 100 = 435.0被转换为435的int时,下面的代码会输出434,对吗?Java舍入问题

这是什么简单的解释,这是一个经常出现的问题?

你会如何解决这个问题?

public class RoundOffDemo { 
    public static void main(String[] args) { 
     double price = 4.35; 
     int cents = (int) (100 * price); // Should be 435 
     System.out.println(cents); // Prints 434! 
    } 
} 
+0

可能的重复[浮点运算不能在Java中产生精确的结果](http://stackoverflow.com/questions/1661273/floating-point-arithmetic-not-产生不精确的-成果在-java的)。还有至少一百个其他问题。 – dan04 2011-12-28 23:46:22

回答

7

问题是4.35不能用二进制精确表示。所以4.35 * 100并不完全是435.0。 (二进制中的每个分数都是2的反幂的和,所有这些都是终止小数。不幸的是,0.35十进制有一个非终止二进制扩展。因此4.35更像4.349999 ... x(其中x是某种东西除此之外,一切都为零,由机器的浮点数的有限表示形式提供)。整数截断然后产生434.你可以使用Math.round()来避免这个问题

+0

另外,我想Math.rint()更适合在这种情况下使用(转换为int)。 – 4ndrew 2011-12-28 17:11:45

+0

@ 4ndrew - 也许如此。人们也可以将参数转换为“float”,以便“round”返回“int”。 (尽管对于OP所使用的数字,使用'float'开始而不是'double'可能是最好的选择。) – 2011-12-28 17:19:37

+2

“真实世界”中的一个很好的例子是1/3,不能准确表示小数。如果计算机使用十进制而不是二进制,则1/3 * 3将为0.9999999,而不是1.0。 – user949300 2011-12-28 18:36:26

-1
  1. 不要在Java中使用双/浮点进行浮点运算,使用的BigDecimal代替。这是因为Java不能精确地表示浮点。

  2. 对于临时变量总是使用BigDecimal,这将在未来的计算中处理/参与。只有在您想将它们保存到数据库中时才将值转换为float/double。

+1

我不会使用BigDecimal作为万能药。它一般不能真正解决舍入问题。例如,如果你划分并且结果不能精确地用十进制表示,那么要么你需要指定舍入模式,否则你会得到一个异常。但是通过指定舍入模式,您又回到了问题中:即使使用BigDecimal,3 *(4/3)!= 4. – 2011-12-28 17:00:27

+1

浮点数的不精确表示并不是继承给Java的问题。而是运行在二进制体系结构上的计算机固有的问题。 – Perception 2011-12-28 17:19:27

0

这是浮点运算的结果。不是一个舍入函数,而是@Ted指出的截断。

由于您正在处理金钱(从您的变量名称中可以看出),请改用BigDecimal。

例如 -

double d = 4.35; 
BigDecimal bd = BigDecimal.valueOf(d); 
bd = bd.multiply(new BigDecimal(100)); 
+1

这并不解决问题; 'd'已经不是4.35,所以'bd'只是将这个舍入误差向前传递。 – 2011-12-28 18:42:35

+0

@Ted - 我实际上已经试过了,并确保它在发布代码之前工作。 BigDecimal.valueOf(d)'使用'Double.toString(d)'并始终产生相同的输出。我在做一个假设,我不应该在这里? – Kal 2011-12-28 19:27:56

+1

在这个地方肯定还有别的东西(可能是四舍五入的)。十进制4.35是二进制100.0(1011)。它不能完全以浮点表示。 – 2011-12-28 19:43:52

0

如果您使用此代码:

System.out.println(price*100); // Prints 434! 

你注意到它的输出是

434.99999999999994

WH ich圆角是434

+0

但是为什么它首先放置了434.99999999999994?我用过的数字不应该给出答案。 – mino 2011-12-28 16:59:30

+0

@ m92 java不是数学家,正如其他人所说的,java在浮点精度方面有一些问题 – 2011-12-28 17:13:08

+1

@AdelBoutros它不是java问题,它是因为double类型的二进制表示。 – 4ndrew 2011-12-28 17:20:25

2

IMO,使用BigDecimal的建议是矫枉过正。对于美分,请使用很长时间。即使是美国国债,也可以用美分来承保。

如果您使用浮点数(或双精度),转换为整数时,请根据需要使用Math.round(),Math.floor()或Math.ceil()。

0

双值4.35实际上表示为4.349999999999999。 100 * 4.349999999999999 = 434.9999999999999。 并将其转换为int值将给你434.

+0

4.3499999999999996447286321199499070644378662109375,确切地说。 – dan04 2011-12-28 23:47:24