将强制转换应用于整数和浮点除法的结果:这里发生了啥?
Posted
技术标签:
【中文标题】将强制转换应用于整数和浮点除法的结果:这里发生了啥?【英文标题】:Applying casts to the results of integer and floating point division: what's going on here?将强制转换应用于整数和浮点除法的结果:这里发生了什么? 【发布时间】:2011-08-04 23:41:23 【问题描述】:我是初学者,有些东西对我来说没有多大意义。请好心解释一下我哪里出错了。如果之前有人问过这个问题,我很抱歉。
这里小数点的存在意味着这些是使用浮点除法计算的。
System.out.println(1/3.0); // this prints: 0.3333333333333333
System.out.println(1.0/3); // this prints: 0.3333333333333333
System.out.println(1.0/3.0); // this prints: 0.3333333333333333
显然,下面是“截断整数除法”的示例。这对我来说似乎有点奇怪,但没关系。
System.out.println(1/3); // this prints: 0
可以这样说:“在下面的行中,(双)演员被评估为第一个。它实际上是说:“将 1/3 视为双 - 不要使用截断整数除法。而是使用浮点除法。""
System.out.println((double)1/3); // this prints: 0.3333333333333333
下面,但是我们得到 0.0 - 这是怎么发生的?
System.out.println((double)(1/3)); // this prints: 0.0
好的,所以可能额外的括号意味着 (1/3) 被评估为第一个。它使用截断整数除法进行评估,产生 0。然后应用双精度,给我们 0.0 Ok,这是有道理的
好的,所以我们也许可以在这里提出一些一般规则:
规则 1:(double) 表达式意味着先应用 (double) 类型转换,然后计算表达式。
规则 2:(double) (expression) 表示计算表达式 然后 应用强制转换。 太好了!
所以在下面的下一行我们有: (int) 表达式,所以我想我们可以应用规则 1)。 (int) cast 被评估为第一个。它有效地表示:“将 1.0/3 视为 int - 不要像使用 double 一样使用尽可能多的内存。不要使用浮点除法,而是应用截断整数除法。”“所以我们有 0 对吗?不.
System.out.println((int)1/3.0); // this prints: 0.3333333333333333
好的,所以我们有 0.33333,所以 (int) 演员表不会被评估为第一个。就好像它不存在一样。让我们提出第三条规则:
规则 3:(int) 表达式意味着完全忽略 (int) 强制转换,只是评估表达式,就像 (int) 甚至不存在一样。
好的,将规则 3 应用于下面的行,我们有 (int) 但我们将忽略它。 1.0/3.0 用浮点除法计算得到 0.3333333。成功!
System.out.println((int)1.0/3.0); // this prints: 0.3333333333333333
在下面的最后一行中,我们再次使用 (int),但我们将忽略它。 (1.0/3) 使用浮点除法计算得出 0.3333333333 对吗?没有。
System.out.println((int)(1.0/3)); // this prints: 0
好吧,我现在很困惑。请你能帮我解决这个问题吗?
【问题讨论】:
不要这样制定自己的规则。一段时间后你会迷惑自己。 【参考方案1】:Java 的语言解析规则基本上是根据其操作顺序 规则在您的代码中随处暗示大量括号。了解 Java 认为括号的位置将有助于您理解这种行为。当你说:
(double) 1 / 3.0
...这相当于说:
((double) 1) / 3.0
这意味着 1 被转换为双精度数,而 3.0 自动成为双精度数,因此您最终将进行浮点除法而不是整数除法。 (double) 1 / 3
也会发生同样的事情,因为除数是 double
,所以即使被除数是 int
,系统也会意识到您想要进行浮点除法。除非您特别要求,否则编译器不希望您丢失精度,因此任何时候被除数或除数为double
,它都会执行double
除法。
另一方面:
(int) 1 / 3.0
... 同说:
((int) 1) / 3.0
在这种情况下,您是在告诉编译器它已经知道什么:除数 (1) 是 int
。然后你要求它除以double
值(3.0)。因为被除数是double
,所以会执行double
除法。
正如您所指出的,1/3
将产生零,因为这就是整数除法的工作方式,并且两个数字都是整数。 1/(int)3.0
也会发生同样的情况,因为您告诉编译器在除法发生之前将 3.0
变为 int
(如在 1/((int)3.0)
中)。
要记住的最后一点是(int) 0.33
也将转换为0
,因为整数不能包含十进制值。所以当你说(int)(1.0/3)
时,你是在做double
除法,但随后你将双精度转换为int
,产生0
。
【讨论】:
啊,好吧,现在说得通了!我认为我的问题中的规则 1 和 2 可能是正确的!但我误解了如何应用演员表——这意味着什么。非常感谢大家花时间帮助我。堆栈溢出规则。【参考方案2】:这是在Java Language Specification - Numeric Promotions中指定的
如果任何操作数是引用类型,则执行拆箱转换(第 5.1.8 节)。
然后:
如果任一操作数是 double 类型,则另一个将转换为 double。
否则,如果任一操作数为浮点类型,则将另一个转换为浮点类型。
否则,如果其中一个操作数是 long 类型,则另一个将转换为 long。
否则,两个操作数都转换为 int 类型。
这个概念叫做拉宽转换:
5.1.2 拓宽基元转换
以下 19 种基本类型的特定转换称为加宽基本类型转换:
byte 到 short、int、long、float 或 double 短于 int、long、float 或 double char 转换为 int、long、float 或 double int 到 long、float 或 double long 到 float 或 double 浮动到两倍
原因
(int) (1.0/3) == 0
是因为你将 1.0/3 的结果(0.33333...按上面的规则)显式地转换为一个整数,相当于“floor(result)”(向下取整),所以结果为 0。
Cast 的优先级高于 /,这就是为什么其他操作会误导您的理解:
(double) 1 / 3
首先会显式地将 1 转换为双精度值 1.0,因此上述规则的结果再次等于 0.3333....
【讨论】:
【参考方案3】:强制转换仅应用于紧随其后的表达式。 示例:
(int)1/3
applies the cast to the 1. equivalent to ((int)1)/3
(int)(1/3)
the cast is applied to the result of the expression.
(int)((double)1.333*(float)3.167)
getting a little more complicated. 1.333 casted to double,
multiplied by 3.167 casted to a float, with the result casted to an int
希望有帮助!
【讨论】:
【参考方案4】:在所有这些情况下都有两个因素:
-
Operator precedence
当除法的参数之一是浮点数时,结果是浮点数。
我会从你的问题中举三个例子:
(double) (1/3)
- 首先计算括号中的表达式 - (1/3) 是一个整数除法,所以结果是 0
,然后将其转换为双精度,你得到 0.0
(int) (1.0/3)
- 首先计算括号中的表达式 - (1.0/3) 是浮点除法,所以结果是 0.3333
,然后将其转换为 int,得到 0
(double) 1/3 - 首先执行转换,所以(double)1
变为 1.0,然后计算除法 1.0/3
- 这是一个浮点除法,所以结果是 0.3333
【讨论】:
以上是关于将强制转换应用于整数和浮点除法的结果:这里发生了啥?的主要内容,如果未能解决你的问题,请参考以下文章