“long x = 1/2”是等于1还是0,为啥? [复制]

Posted

技术标签:

【中文标题】“long x = 1/2”是等于1还是0,为啥? [复制]【英文标题】:Is "long x = 1/2" equal to 1 or 0, and why? [duplicate]“long x = 1/2”是等于1还是0,为什么? [复制] 【发布时间】:2010-02-02 20:33:51 【问题描述】:

如果我有类似的东西:

long x = 1/2;

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

【问题讨论】:

请注意,在您的标题中,您在“2”之后有一个句点,这会导致浮点常量和不同的结果...我知道您的意思是句子上的标点符号,但我发现它令人困惑。 BigDecimal 支持五种取整模式,为什么只能选择向上取整? 好的,我假设您阅读了有关“/”运算符的 C/C++ 文档,是吗?...这不是一个奇怪的问题吗? 【参考方案1】:

它进行整数除法,将所有内容截断到小数点右侧。

【讨论】:

如果您使用的是 C 并且您有负数,则该声明不起作用。从 C++ 中,我得到了 -5/2=-2(向零舍入)。如果您检查二进制补码,您会发现这不是简单的截断 - 这将给出 -5/2=-3。我怀疑 C++ 结果实际上是实现定义的。 @Steve314:是的。 C++ 标准为您提供(a / b) * b + a % b == a,并保证如果a 和b 都是正数,a / ba % b 将是非负数。如果 a 和/或 b 为负数,则符号是实现定义的(标准,5.6)。我手头没有 C 或 Java 标准文档。 它完全用C指定(我想这是由C++继承的):When integers are divided, the result of the / operator is the algebraic quotient with any fractional part discarded.(即,它截断,但截断是针对数值定义的,而不是底层表示)。 @caf - 如果你说的是真的,这是一个最近的标准,它定义了以前的标准未定义的内容。它当前未在 C++ 中定义的事实是因为在复制标准时它在 C 中未定义。 这是真的 - 它是在 C89 中实现定义的。【参考方案2】:

整数除法起源于数论。当你做 1/2 时,你是在问 2 等于 1 多少次?答案永远不会,因此等式变为 0*2 + 1 = 1,其中 0 是商(从 1/2 得到的),1 是余数(从 1%2 得到的)。

正确地指出 % 不是数学意义上的真正模数,而是除法的余数。处理负整数时会有所不同。

希望对您有所帮助。

【讨论】:

至少在 C++ 中它可以是一个真正的模数;这是实现定义的。面对现实,没有办法处理整数除法和负数的余数,每个人都会觉得很直观。【参考方案3】:

这个表达式的作用是它首先声明一个 long 称为 x 的存在,然后将右侧表达式的值分配给它。右手边的表达式是 1/2,因为 1 和 2 都是整数,所以这被解释为整数除法。整数除法的结果总是一个整数,所以沿着 5/3 行的东西将返回 1,因为只有一个三适合一个五。那么对于 1/2,有多少个 2 可以放入 1 中? 0.

如果您编写类似的内容,这在某些语言中可能会产生一些有趣的输出 双倍 x = 1/2。在这种情况下,您可能期望 0.5,但它通常会先计算右侧的整数值,然后再分配并将结果转换为双精度值,给出值 0.0

需要注意的是,在进行这种类型转换时,它永远不会对结果进行四舍五入。所以如果你做相反的事情: 长 x = (长)(1.0/2.0); 然后虽然 (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...

【讨论】:

不,将浮点数分配给整数 still 会截断。当然,关于整数除法如何从不会导致任何浮点开始,你的其余部分仍然有效。 @Chris:是的,我刚刚检查了确切的规则。这不是我每天都依赖的东西。谢谢 我想你会经常看到像long x = 1.0 / 2.0 + 0.5 这样的东西,我想如果你得到 0.499999999999997... 结果也无济于事(在这种情况下这会让我非常惊讶),但如果是这样,除了不将浮点数舍入为整数之外,可能没有任何帮助。 @UncleBens:为什么会让你大吃一惊? @jambjo:看到 1.0、2.0 和 0.5 都可以精确表示(无论调用浮点的典型标准),并且测试时结果为 1,正如预期的那样 :) 我'并不是说它适用于所有输入,但如果不是,我看不到如何可靠地舍入浮点数。 (基本上,如果重要的话,根本不应该使用浮点数。)【参考方案5】:

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

double x = (double)1/2;

你会得到0.5的值。

【讨论】:

【参考方案6】:

有许多不同的舍入约定,最常见的是向 +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

这是向 -infinity 四舍五入,这就是 Python 所做的(或至少它在 Python 2.5 中所做的)。

如果我们使用符号幅度表示,截断是正确的词,但二进制补码几十年来一直是事实上的标准。

在 C 和 C++ 中,我希望虽然它通常被称为截断,但实际上这个细节在标准中没有定义并留给实现 - 允许编译器为平台使用最简单和最快的方法的借口(什么处理器除法指令自然会这样做)。如果你有负数,这只是一个问题 - 我还没有看到任何可以给出 5/2 = 3 的语言或实现。

我不知道 Java 标准是怎么说的。 Python 手册指定了“下限”除法,这是舍入到 -infinity 的常用术语。

编辑

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

人们倾向于假设余数和模数相同,但 WRT 符号值可能不同 - 取决于舍入规则。根据定义,模值永远不会是负数,但余数可以是负数。

我怀疑 Python 舍入负无穷大规则旨在确保单个 % 运算符作为余数和模数均有效。在 C 和 C++ 中,% 的含义(余数或模数)是(是的,您猜对了)实现定义。

Ada 实际上有两个独立的运算符 - mod 和 rem。需要将除法四舍五入到零,因此 mod 和 rem do 给出不同的结果。

【讨论】:

C++ 标准要求,对于正整数 a 和 b,a % b 是非负数,所以 5 / 3 不可能是 3,因为那时 5 % 3 必须是 -4。它建议用负数将商四舍五入到零,但这是实现定义的。

以上是关于“long x = 1/2”是等于1还是0,为啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥 ~-1 等于 0 而 ~1 等于 -2?

SQl2008中主键不能等于NULL,可是可以等于null,这是为啥。

在EXCEL中用“CORREL”函数求 两列的相关系数为啥是等于1?

为啥双 1.0 等于 int 1?

SQl2008中主键不能等于NULL,可是可以等于null,这是为啥。

为啥这无法进入中断