整数除法:为什么1/3的结果是0?

152

我正在编写这段代码:

public static void main(String[] args) {
    double g = 1 / 3;
    System.out.printf("%.2f", g);
}
结果为0。为什么会这样,我该如何解决这个问题?

已经有很多关于这个问题的讨论了,但是在这里你可以阅读更多相关内容的来源:https://feb21011.ese.eur.nl/week-1/6-calculating(舍入的段落)。 - Marja van der Wind
19个回答

190

这两个操作数(1和3)是整数,因此使用整数算术(在这里是除法)。将结果变量声明为double只会导致隐式转换发生在除法后

当然,整数除法返回向零舍入的除法真正结果。因此,在这里,0.333...的结果被向下舍入为0。(注意,处理器实际上并没有进行任何舍入,但您仍可以这样考虑。)

此外,请注意,如果两个操作数(数字)都以浮点数给出;3.0和1.0,或者仅第一个,则使用浮点运算,从而给出0.333...


42
+1 总是向下取整。 int i = .99999999 将 int 设为 0。更具体地说,它取整数部分并丢弃剩余部分。 - Byron Whitlock
48
它朝着零舍入,对于大于零的值,“向下”舍入。(+0.9舍入为0,-0.9也舍入为0。) - Adrian Smith
@Byron:没错,完全正确。我不认为处理器实际上会进行任何舍入,因为除法的实现方式非常不同,但这样想很方便。 - Noldorin
20
不,不是四舍五入:截断 - Basil Bourque
4
在Java术语中,rounding DOWN是指向零舍入。FLOOR舍入是指向负无穷大舍入。 - Andreas
参考Java 8 JLS的15.7.2。其他版本将在相应的Division Operator部分中包含此内容。 - Jason C

32

1/3 使用整数除法,因为两边都是整数。

你需要至少一个是 floatdouble

如果你在代码中输入值,就像你的问题一样,你可以这样做:1.0/3 ; 这里的 1.0 是一个 double。

如果你从其他地方获取值,你可以使用 (double)int 转换成 double

int x = ...;
int y = ...;
double value = ((double) x) / y;

9

将其明确转换为 double

double g = 1.0/3.0

这是因为Java使用整数除法运算符对13进行计算,因为它们被输入为整数常量。

如果您使用的是变量而不是硬编码数字,您也可以通过乘以1.0来实现此操作。 - Marja van der Wind

4

结果为0。为什么会这样,如何解决这个问题?

简短回答:

您可以通过以下方式解决:

double g = 1.0/3.0; 

或者

double g = 1.0/3; 

或者

double g = 1/3.0; 

或者

double g = (double) 1 / 3;

这些选项中的最后一个是在使用变量时需要的,例如: int a = 1, b = 3; double g = (double) a / b;
更完整的答案如下:

double g = 1 / 3;

这将导致结果为0,因为:
  • 被除数小于除数;
  • 两个变量都是int类型,因此结果是int类型(参见5.6.2. JLS),自然不能表示浮点值,如0.333333..
  • “整数除法向0舍入。”15.17.2 JLS
为什么double g = 1.0/3.0;double g = ((double) 1) / 3;有效?
第5章。转换和提升可以看到:

一个转换上下文是数值运算符的操作数,如+或*。这种操作数的转换过程称为数值提升。提升是特殊的,因为在二元运算符的情况下,选择一个操作数的转换可能部分取决于其他操作数表达式的类型。

5.6.2. 二进制数值提升

当运算符将一对操作数应用于二进制数值提升时,每个操作数都必须表示可转换为数值类型的值,则按照以下规则依次应用:

如果任何操作数是引用类型,则它将经受拆箱转换(§5.1.8)。

扩展原始转换(§5.1.2)被用来将一个或两个操作数转换为指定的类型,具体取决于以下规则:

如果任一操作数为double类型,则另一个操作数将被转换为double类型。

否则,如果任一操作数为float类型,则另一个操作数将被转换为float类型。

否则,如果任一操作数为long类型,则另一个操作数将被转换为long类型。

否则,两个操作数都将被转换为int类型。


4
最简单的解决方法就是这样做。
double g = (double) 1 / 3;

由于您没有输入 1.0 / 3.0,这个操作会让您手动将它转换为 double 数据类型,因为 Java 默认认为这是整数除法,即使这意味着缩小转换。这就是所谓的强制类型转换运算符。

在这里我们只对一个操作数进行了类型转换,这已足够避免整数除法(朝向零舍入)


4

因为你正在进行整数除法。

正如@Noldorin所说,如果两个运算符都是整数,则使用整数除法。

结果0.33333333无法表示为整数,因此只有整数部分(0)被赋给结果。

如果任何一个运算符是double/float,那么将进行浮点运算。但是如果你这样做,你会遇到同样的问题:

int n = 1.0 / 3.0;

2

你应该使用:

double g=1.0/3;

或者

double g=1/3.0;

整数除法返回整数。


2

我做了这件事。

double g = 1.0/3.0;
System.out.printf("%gf", g);

在进行双精度计算时,请使用 .0,否则Java将假定您正在使用整数。如果计算使用任何数量的双精度值,则输出将为双精度值。如果它们都是整数,则输出将为整数。

2
在JAVA中进行转换非常简单,但需要一些理解。如整数操作在JLS中所述:

如果除移位运算符以外的整数运算符至少有一个操作数是long类型,则使用64位精度执行操作,并且数字运算符的结果为long类型。如果另一个操作数不是long型,则通过数值提升(§5.6)首先将其扩展(§5.1.5)为long型。

而例子总是最好的翻译方式 ;)
int + long -> long
int(1) + long(2) + int(3) -> long(1+2) + long(3)

否则,使用32位精度执行操作,并且数值运算符的结果为int类型。如果任一操作数不是int类型,则通过数字提升首先将其扩展为int类型。
short + int -> int + int -> int

使用Eclipse的小例子展示即使是两个short相加也不会那么容易:
short s = 1;
s = s + s; <- Compiling error

//possible loss of precision
//  required: short
//  found:    int

这将需要进行一次转换,可能会导致精度损失。

浮点运算符也是如此

如果数值运算符的至少一个操作数是double类型,则使用64位浮点算术执行该操作,并且数值运算符的结果是double类型的值。如果另一个操作数不是double,则通过数值提升(§5.6)将其首先扩展(§5.1.5)为double类型。

因此,在float中进行了提升到double的操作。

当整数和浮点值混合时,结果为浮点值

如果二元运算符的至少一个操作数是浮点类型,则该操作是浮点操作,即使另一个操作数是整数也是如此。

这对于二元运算符是正确的,但不适用于“赋值运算符”例如+=

一个简单的工作示例就足以证明这一点

int i = 1;
i += 1.5f;

原因是这里进行了隐式转换,它将会像这样执行
i = (int) i + 1.5f
i = (int) 2.5f
i = 2

1

将1转换为浮点数,即可使用浮点数除法

public static void main(String d[]){
    double g=1f/3;
    System.out.printf("%.2f",g);
}

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