整数除法:您如何产生双倍?


249

对于此代码块:

int num = 5;
int denom = 7;
double d = num / denom;

的值d就是0.0。它可以通过强制转换来工作:

double d = ((double) num) / denom;

但是还有另一种方法来获得正确的double结果吗?我不喜欢强制转换基元,谁知道会发生什么。



9
将“ int”强制转换为双精度是安全的,您将始终获得相同的值而不会损失精度。
彼得·劳瑞

1
我想知道以下是编译器针对除法采取的正确步骤:1)将num转换为浮点数2)将denom转换为浮点数2)将num除以denom。如果我不正确,请告诉我。
mannyee 2014年

Answers:


156
double num = 5;

这样可以避免强制转换。但是您会发现强制转换的定义很明确。您不必猜测,只需检查JLS即可。从int到double是一个不断扩大的转换。从第5.1.2节开始

扩展原始转换不会丢失有关数值总体大小的信息。

[...]

将int或long值转换为float或将long值转换为double可能会导致精度损失,也就是说,结果可能会丢失该值的某些最低有效位。在这种情况下,使用IEEE 754舍入到最近模式(第4.2.4节),结果浮点值将是整数值的正确舍入版本。

5可以精确表示为双精度。


60
+1。别再害怕铸造了。了解其工作原理。一切都明确。
Mark Peters

1
@FabricioPH这适用于所有情况,并且与* 1.0解决方案相同。
Vitruvius 2014年

3
@Saposhiente它超出了工作范围。对我来说,更改变量的类型似乎是肮脏的代码。如果您必须具有整数,并且为了能够执行数学运算而更改浮点数的表示形式,则可能会冒险冒险。同样,在某些情况下,将变量读取为整数会使代码更易于理解。
Fabricio PH'3

2
@FabricioPH,乘以1.0仍然会改变结果的类型,只是以不同的方式。“对我来说似乎是肮脏的代码”并不能解释您认为乘以哪种情况1.0实际上更好(或者为什么这样)。
马修·弗莱申

4
@FabricioPH您和OP似乎在错误的信念下工作(您在这里说“更改变量的类型”),认为这样做会(double)num有所变化num。它不是-它为语句的上下文创建一个临时的double。
Paul Tomblin,2015年

100

强制转换基元有什么问题?

如果您出于某些原因不想进行投射,可以这样做

double d = num * 1.0 / denom;

63
...在乘法之前进行隐式转换
爱丽丝·珀塞尔

2
在大多数情况下,这比更改其他变量的类型更好。
Fabricio PH

3
@FabricioPH除非您有引用的来源,否则没有任何东西暗示这是真的。
Vitruvius 2014年

1
@Saposhiente在您的其他类似评论中回复
Fabricio PH

1
您还可以使用“ D”后缀(执行相同的隐式强制转换);-举例来说:double d = num * 1D / denom;
BrainSlugs83

73

我不喜欢强制转换基元,谁知道会发生什么。

为什么对转换基元有非理性的恐惧?当你施放一个会发生什么不好intdouble。如果您不确定它的工作方式,请在Java语言规范中进行查找。转换为intto double是一个不断扩大的原始转换

您可以通过使用分母而不是分子来消除多余的一对括号:

double d = num / (double) denom;

2
您也可以做double d = (double) num / denom;...(确定,这取决于优先级)
user85421'6

27

如果您更改一个变量的类型,则您必须记住,如果您的公式发生更改,则必须再次潜入两倍,因为如果此变量不再是计算的一部分,结果将被弄乱。我习惯在计算中进行强制转换,并在其旁边添加注释。

double d = 5 / (double) 20; //cast to double, to do floating point calculations

请注意,强制转换结果不会

double d = (double)(5 / 20); //produces 0.0

17

将整数之一或整数两者都强制转换为浮点数,以强制执行浮点运算Math的操作。否则,始终首选整数Math。所以:

1. double d = (double)5 / 20;
2. double v = (double)5 / (double) 20;
3. double v = 5 / (double) 20;

请注意,强制转换结果不会这样做。因为首先除法是根据优先级规则完成的。

double d = (double)(5 / 20); //produces 0.0

我认为您正在考虑的转换没有任何问题。


10

类型转换是唯一的方法


double从整数除法产生a- 没有其他方法可以不进行强制转换(也许您不会明确地执行它,但它会发生)。

现在,有几种方法可以尝试获取精确的double值(where numdenomare int类型,以及使用铸造的过程)-

  1. 使用显式强制转换:

    • double d = (double) num / denom;
    • double d = ((double) num) / denom;
    • double d = num / (double) denom;
    • double d = (double) num / (double) denom;

但不是 double d = (double) (num / denom);

  1. 使用隐式强制转换:

    • double d = num * 1.0 / denom;
    • double d = num / 1d / denom;
    • double d = ( num + 0.0 ) / denom;
    • double d = num; d /= denom;

但不是double d = num / denom * 1.0;
,不是double d = 0.0 + ( num / denom );



0

您可以考虑包装操作。例如:

class Utils
{
    public static double divide(int num, int denom) {
        return ((double) num) / denom;
    }
}

这使您可以(一次)查找演员表是否完全符合您的要求。此方法也可能要经过测试,以确保它可以继续执行您想要的操作。只要能产生正确的结果,使用何种技巧进行除法也无关紧要(您可以在此处使用任何答案)。现在,您需要将两个整数相除的任何地方,都可以调用Utils::divide并信任它可以做正确的事情。



By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.