“long x = 1/2”等于1还是0,为什么?

9

如果我有这样的内容:

long x = 1/2;

这不应该被舍入到1吗?当我在屏幕上打印时,它显示为0。


1
请注意,在您的标题中,数字“2”后面有一个句点,这会导致浮点常量和不同的结果...我知道您是想将其作为句子标点符号,但我觉得这很令人困惑。 - dmckee --- ex-moderator kitten
BigDecimal支持五种舍入模式,为什么只能选择向上取整? - Peter Lawrey
好的,我假设你已经阅读了关于“/”运算符的C/C++文档,是吗?...这不是一个奇怪的问题吗? - botismarius
6个回答

30

这里进行整数除法,会将小数点右侧的所有内容截断。


如果您使用 C 且有负数,那么该声明就不起作用了。从 C++ 中,我得到了 -5/2=-2(朝零舍入)。如果您在二进制补码中检查,就会发现这并非简单截断——这将给出 -5/2=-3。我怀疑 C++ 的结果实际上是实现定义的。 - user180247
1
@Steve314:是的。C++标准确保了(a / b) * b + a % b == a,并且保证如果a和b都是正数,则a / ba % b将是非负数。如果a和/或b为负,则符号是实现定义的(标准,5.6)。我手头没有C或Java标准文档。 - David Thornley
1
它在C语言中完全被指定(我想这也被C++继承):“当整数相除时,/运算符的结果是代数商,任何小数部分都会被舍弃。”(即,它是截断,但是截断是针对数值定义的,而不是底层表示)。 - caf
2
这是真的 - 在C89中它是实现定义的。 - caf
@Steve: -5/2 = -2.5,通过整数除法,0.5被截断,得到-2,这个答案完全正确。 - Lawrence Dol
显示剩余2条评论

6
整数除法起源于数论。当你做1/2时,你在问2等于1多少次?答案永远是0,因此方程变为0*2 + 1 = 1,其中0是商(从1/2得到的)而1是余数(从1%2得到的)。
需要指出的是,在数学意义上,%不是真正的模运算,而总是来自除法的余数。当你处理负整数时会有所差异。
希望这能帮到你。

1
至少在C++中,它可以是真正的模数;这是实现定义的。面对现实吧,没有一种处理带有负数的整数除法和余数的方式是每个人都会觉得直观的。 - David Thornley

5
这个表达式首先声明了一个名为x的长整型变量,然后将其赋值为右侧表达式的值。右侧表达式是1/2,由于1和2都是整数,因此这被解释为整数除法。使用整数除法,结果始终是一个整数,所以类似于5/3的结果将返回1,因为只有一个3适合于5。因此,在1/2中,有多少个2适合于1?0。
在某些语言中,如果您编写类似于double x = 1/2的内容,则可能会导致一些有趣的输出。在这种情况下,您可能期望得到0.5,但通常会先计算右侧的整数值,然后将结果分配并转换为double类型,从而得到值0.0。
需要注意的是,在进行这种类型转换时,它永远不会舍入结果。因此,如果您执行相反的操作: long x = (long)(1.0/2.0); 那么while(1.0/2.0)将计算为0.5,但(long)强制转换将强制将其截断为0。即使我有long x = (long)(0.9),结果仍将是0。它只是在小数点后截断。

4

它不能四舍五入,因为它从未处于可以四舍五入的状态

表达式“1/2”在分配给long之前从未是0.5

现在,long x = 1.0/2.0 因为在分配之前右侧的表达式有效进行四舍五入。除非你得到0.499999999999997...


6
不,将浮点数分配给整数仍然会截断。当然,你提到的整数除法永远不会产生任何浮点数的观点仍然是正确的。 - C. K. Young
@Chris:是的,我刚刚检查了准确的规则。这不是我日常依赖的东西。谢谢。 - gbn
1
我想你经常会看到这样的代码:long x = 1.0 / 2.0 + 0.5。虽然在这种情况下得到0.499999999999997...作为结果可能会让人感到惊讶,但即使如此,这也不会有所帮助。除非不将浮点数四舍五入为整数,否则可能没有任何方法可以解决这个问题。 - UncleBens
@jambjo:这让我很惊讶,因为0.5可以在二进制浮点数中精确表示,没有什么特别的操作。而像(1.0/6.0)*3.0+0.5这样使用了二进制中无法精确表示的数字,那么得到的结果就不准确了,这种情况下我就不会感到惊讶了。 - David Thornley
这让我想起了一次争论,有人试图告诉我,某个构建有缺陷的东西并不是坏的,因为它从来没有工作过。自然语言并不那么迂腐。过去分词经常被用作形容词,有时暗示着过去状态的转变,但同样经常只描述当前状态。结果要舍入并不需要机器必须生成一个中间未舍入的结果。 - user180247
显示剩余2条评论

2

这个问题之前已经在这个网站上回答过了,你正在进行整数除法,如果你想得到0.5,请使用以下方法:

double x = (double)1/2;

你将会得到值为0.5


1

有很多不同的舍入约定,最常见的是向+inf舍入、向-inf舍入和向零舍入。许多人认为只有一种正确的方法,但他们对这种方法有不同的看法 ;-)

整数除法没有中间的非整数结果,但当然是确定性地进行除法运算,并且对于特定的平台和编译器,始终遵循一种特定的舍入约定。

使用Visual C++,我得到5/2=2和-5/2=-2,向零舍入。

C、C++和Java中的舍入通常称为“截断”——意思是删除不需要的位。但这可能会误导。使用4位2s补码二进制,执行截断所暗示的操作得到...

 5/2 = 0101/0010 = 0010.1 --> 0010 =  2
-5/2 = 1011/0010 = 1101.1 --> 1101 = -3

哪种舍入方式是向负无穷大取整,这也是Python所做的(或者至少是Python 2.5中所做的)。

如果我们使用符号-幅度表示,那么截断将是正确的词语,但是二进制补码已成为事实上的标准数十年。

在C和C++中,我期望虽然通常称为截断,但实际上此细节在标准中是未定义的,并留给实现 - 这是允许编译器在平台上使用最简单和最快的方法的借口(处理器除法指令自然执行的操作)。但只有当你有负数时才会出现问题 - 我还没有看到任何语言或实现会给出5/2 = 3。

我不知道Java标准说什么。 Python手册指定“floor”除法,这是一个常用的朝向负无穷大取整的术语。

编辑

额外说明-根据定义,如果a / b = c余数d,则a =(b * c)+ d。为了使其成立,您必须选择适合您的舍入约定的余数。

人们往往认为余数和模数是相同的,但对于带符号值,它们可以不同-具体取决于舍入规则。模数值定义永远不为负,但余数可以为负。

我怀疑 Python 的向负无穷取整规则是为了确保单个 % 运算符既可以作为余数又可以作为模运算符。在 C 和 C++ 中,% 的含义(余数或模)是(没错,你猜对了)实现定义的。

Ada 实际上有两个不同的运算符 - mod 和 rem。由于除法需要向零舍入,所以 mod 和 rem 给出不同的结果。


C++标准要求对于正整数a和b,a % b必须是非负的,因此5 / 3不可能是3,否则5 % 3将会是-4。它建议在处理负数时向零舍入商,但这是由实现定义的。 - David Thornley

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