整数除法:如何得到一个双精度浮点数?

307

对于此代码块:

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

d的值为0.0。可以通过强制类型转换来让它发挥作用:

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

但是是否有另一种方法可以得到正确的double结果呢?我不喜欢将基本类型强制转换,因为谁知道会发生什么。


14
将“int”转换为“double”是安全的,您始终会获得相同的值且不会丢失精度。 - Peter Lawrey
1
我想知道编译器在执行除法时是否采取了以下正确步骤:1)将num转换为float 2)同样将denom转换为float 3)将num除以denom。如果我有错误,请告诉我。 - mannyee
11个回答

192
double num = 5;

这避免了强制类型转换。但是,你会发现强制类型转换是明确定义的。你不必猜测,只需查看JLS。int到double是扩展转换。来自§5.1.2
扩展原始类型转换不会丢失有关数值总体大小的信息。
将int或long值转换为float,或将long值转换为double,可能会导致精度损失--也就是说,结果可能会丢失一些最低有效位的值。在这种情况下,使用IEEE 754舍入至最近模式(§4.2.4),生成的浮点值将是整数值的正确舍入版本。
5可以被准确地表示为一个double。

74
不要害怕投掷。学习它的工作原理。一切都很明确。 - Mark Peters
1
@FabricioPH 这适用于任何情况,并且与*1.0解决方案完全相同。 - Vitruvie
3
@Saposhiente 这不仅仅是工作与否的问题。改变变量类型对我来说似乎是肮脏的代码。如果你有一个必须是整数类型的东西,并且你改变了浮点数表示形式只是为了能够执行数学运算,那么你可能会冒险。此外,在某些情况下,将变量读取为整数可以使代码更易于理解。 - Rudolf Real
2
@FabricioPH,即使通过乘以1.0仍然会以不同的方式更改结果的类型。 “对我来说似乎是肮脏的代码”并没有解释您认为在哪种情况下乘以1.0实际上更好(或者为什么可能更好)。 - Matthew Flaschen
5
@FabricioPH,您和OP似乎都存在一种错误的信念(在您说“更改变量类型”时),即执行(double)num会以某种方式更改num。实际上并不是这样-它只是为语句的上下文创建了一个临时double。 - Paul Tomblin
显示剩余3条评论

125

把原始类型强制转换使用起来有什么问题吗?

如果你不想强制转换,你可以这样做:

double d = num * 1.0 / denom;

76
在执行乘法操作前,会进行隐式类型转换。 - Alice Purcell
2
在大多数情况下,这比更改其他变量的类型要好。 - Rudolf Real
4
@FabricioPH,除非你有引用的来源,否则没有任何证据表明这是真的。 - Vitruvie
1
@Saposhiente 在你的另一个类似的评论中回复了。 - Rudolf Real
2
你也可以使用“D”后缀(执行相同的隐式转换); -- 所以例如:double d = num * 1D / denom; - BrainSlugs83

89

我不喜欢将基本类型进行强制转换,谁知道会发生什么事情。

为什么你对强制转换基本类型有一种无理的恐惧呢?当你把一个int转换成 double时并不会发生什么坏事。如果你只是不确定它的工作原理,可以在Java语言规范中查找。将一个int转换成double是一种扩大基本类型转换

你可以通过将分母进行强制转换来消除额外的括号:

double d = num / (double) denom;

3
你同样可以执行 double d = (double) num / denom;...(好的,这取决于优先级)。 - user85421

34

如果你更改了一个变量的类型,那么如果你的公式发生变化,你就需要记得再次加入一个double,因为如果这个变量不再参与计算,结果就会出错。我有一个习惯在计算中进行类型转换,并在其旁边添加注释。

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

请注意,对结果进行转换并不能解决问题。

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

27

类型转换是唯一的方法

也许你不会显式地进行类型转换,但它会发生。

现在,我们有几种方法可以尝试获得double值(其中numdenomint类型,并且当然是使用强制转换) -

  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);


现在,如果你问:哪个更好?显式的?还是隐式的?

好吧,不要给出一个直接的答案。只需记住:我们程序员不喜欢源代码中的惊喜或魔法。而且,额外的操作肯定不会使您的代码更有效率。对吧?


1
这也适用于浮点数吗? - Quimbo

22

将其中一个整数或两个整数都转换为浮点数,以强制使用浮点数进行计算。否则,始终会优先选择整数运算。因此:

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

我认为你所考虑的铸造方式并没有任何问题。


2

最好的方法是

int i = 3;
Double d = i * 1.0;

d is 3.0 now.

2

使用类似以下的内容:

double step = 1d / 5;

(1d 是将数字 1 转换为 double 类型)


10
1d不是一个强制类型转换,它只是一个声明。 - Sefier Tang

0
您可以考虑对操作进行封装。例如:
class Utils
{
    public static double divide(int num, int denom) {
        return ((double) num) / denom;
    }
}

这样可以让你查找(仅一次)强制转换是否完全符合你的要求。该方法也可以接受测试,以确保它继续按照你的意愿执行。无论你使用什么技巧来引起除法(你可以使用这里的任何答案),只要它产生正确的结果即可。在任何需要除两个整数的地方,现在你只需调用Utils::divide并相信它会做正确的事情。


0

只需添加“D”。

int i = 6;
double d = i / 2D; // This will divide bei double.
System.out.println(d); // This will print a double. = 3D

通常,小数点后缀“.0”对人类来说更清晰。 d 是下一个最好的选择。我认为 D 有害,因为它看起来太像数字,容易与数字相邻。 - AndrewF

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接