如何准确判断double是否为整数?

特别是在Java中,如何确定double是否为整数? 为了澄清,我想知道如何确定double实际上不包含任何分数或小数。

我主要关注的是浮点数的性质。 我想到的方法(以及我通过Google找到的方法)基本遵循以下格式:

 double d = 1.0; if((int)d == d) { //do stuff } else { // ... } 

我当然不是浮点数和它们的行为方面的专家,但我的印象是因为double存储只是数字的近似值if()条件只会进入某些时间(甚至可能是大部分时间)。 但我正在寻找一种方法,无论double值如何存储在系统中,该方法都可以保证100%的工作时间。

这可能吗? 如果是这样,怎么样和为什么?

double可以存储某些值的精确表示,例如小整数和(负或正)2的幂。

如果它确实存储了一个精确的整数,那么((int)d == d)工作正常。 实际上,对于任何32位整数i, (int)((double)i) == i因为double可以精确地表示它。

请注意,对于非常大的数字(大小大于约2 ** 52),double将始终显示为整数,因为它将不再能够存储任何小数部分。 例如,如果您尝试强制转换为Java,则会产生影响。

 if(new BigDecimal(d).scale() <= 0) { //do stuff } 

使用if((int)d == d)应始终适用于任何32位整数。 为了使它最多可以工作64位,你可以使用if((long)d == d ,除了它可以解释更大的数量之外,它实际上是相同的。如果d大于最大long值(或者小于最小值,然后保证是一个精确的整数。测试d是否为整数的函数可以构造如下:

 boolean isInteger(double d){ if(d > Long.MAX_VALUE || d < Long.MIN_VALUE){ return true; } else if((long)d == d){ return true; } else { return false; } } 

如果浮点数是整数,那么它就是该整数的精确表示。

怎么样

  if(d % 1 == 0) 

这是有效的,因为所有整数都是0模1。

编辑对于那些因为速度慢而反对这一点的人,我对它进行了分析,发现它比铸造慢约3.5倍。 除非这是一个紧密的循环,否则我会说这是一种更好的解决方法,因为它非常清楚你正在测试什么,并且不需要任何关于整数转换的语义。

我通过在javac上运行时间来描述它

 class modulo { public static void main(String[] args) { long successes = 0; for(double i = 0.0; i < Integer.MAX_VALUE; i+= 0.125) { if(i % 1 == 0) successes++; } System.out.println(successes); } } 

VS

 class cast { public static void main(String[] args) { long successes = 0; for(double i = 0.0; i < Integer.MAX_VALUE; i+= 0.125) { if((int)i == i) successes++; } System.out.println(successes); } } 

最后两者都印有2147483647。
Modulo在我的机器上花了189.99s - Cast花了54.75s。

双打是具有二进制指数的二进制分数。 您不能确定整数可以精确地表示为double,尤其是如果它是从其他值计算的话。

因此,接近这一点的常规方法是说它需要“足够接近”整数值,其中足够接近通常意味着“在X%内”(其中X相当小)。

即如果X为1,那么1.98和2.02都将被认为足够接近2.如果X为0.01则需要在1.9998和2.0002之间足够接近。