将强制转换应用于整数和浮点除法的结果:这里发生了啥?

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

【讨论】:

以上是关于将强制转换应用于整数和浮点除法的结果:这里发生了啥?的主要内容,如果未能解决你的问题,请参考以下文章

python3中整数和小数的转换

解决“浮点上下文中的整数除法”警告

整数与浮点除法 -> 谁负责提供结果?

python中x//10啥意思

java中整除和浮点除啥区别

为啥从 int 转换为浮点值?