简体   繁体   English

C/C++ 中的整数除法会遇到精度问题吗?

[英]Can integer division in C/C++ run into loss of precision issues?

Suppose we have three integer (int, long, long long, unsigned int, etc) variables a, b, c .假设我们有三个整数(int、long、long long、unsigned int 等)变量a, b, c Normally, performing通常,执行

c = a / b;

would result in truncate of fractions.将导致截断分数。 However, is it possible for c to end up with an incorrect value?但是,c 是否有可能以不正确的值结束?

I am not talking about a / b may be out of range for c's type.我不是在谈论 a / b 可能超出c's type.的范围c's type. Rather, I am talking about how integer division is implemented in C. Does performing a / b first generate a float type intermediate result, and then the intermediate value is truncated?而是说C中是如何实现整数除法的,执行a / b是不是先生a / b一个浮点型的中间结果,然后中间值被截断?

If so, I wonder if loss of precision of the intermediate value can lead to an incorrect value of c.如果是这样,我想知道中间值的精度损失是否会导致 c 的值不正确。 For example, suppose the precise value for a / b is 2, but somehow the intermediate result is 1.9999... , thus c will end up with an incorrect value of 1. Can such cases happen, or does integer division always result in a correct value if the expected value is in the range of c's type?例如,假设 a / b 的精确值是 2,但不知何故中间结果是1.9999... ,因此 c 最终会得到不正确的值 1。这种情况会发生吗,或者整数除法总是导致 a如果预期值在 c 的类型范围内,则值是否正确?

Does performing a / b first generate a float type intermediate result执行 a/b 是否先生成浮点型中间结果

As far as the language is concerned, there are no intermediate results.就语言而言,没有中间结果。

does integer division always result in a correct value if the expected value is in the range of c's type?如果预期值在 c 类型的范围内,整数除法是否总是产生正确的值?

Yes.是的。

Section 6.5.5 of the C11 standards states C11 标准的第 6.5.5 节规定

When integers are divided, the result of the / operator is the algebraic quotient with any fractional part discarded.当整数被除时, /运算符的结果是代数商,其中任何小数部分都被丢弃。 If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a;如果商a/b是可表示的,则表达式(a/b)*b + a%b应等于a;

Which means there's no way, mathematically, that you'll get wrong results.这意味着从数学上讲,您不可能得到错误的结果。

Suppose we have three integer (int, long, long long, unsigned int, etc) variables a, b, c.假设我们有三个整数(int、long、long long、unsigned int 等)变量 a、b、c。 Normally, performing通常,执行

c = a / b;

would result in truncate of fractions.将导致截断分数。 However, is it possible for c to end up with an incorrect value?但是, c是否有可能以不正确的值结束? I am not talking about a / b may be out of range for c's type.我不是在谈论 a / b 可能超出 c 类型的范围。

It should not be possible that for example the last digit of division be wrong , if all rules were followed otherwise.例如,除法的最后一位数字不应该是错误的,如果其他所有规则都被遵守的话。 C11 6.5.5p6 : C11 6.5.5p6

When integers are divided, the result of the / operator is the algebraic quotient with any fractional part discarded.当整数被除时, /运算符的结果是代数商,其中任何小数部分都被丢弃。

ie the result is not "close" to but exactly the same as a / b would be algebraically, just anything following the point discarded.即结果不是“接近”但与 a / b 在代数上完全相同,只是丢弃该点之后的任何内容。

That does not mean there won't be any gotchas: it is possible that the division of a / b might be mathematically not out of range for c 's type yet out of range for the type used in the division itself which can cause wrong values be set in c.这并不意味着不会有任何问题: a / b的除法可能在数学上没有超出c类型的范围,但超出除法本身使用的类型的范围,这可能会导致错误值在 c 中设置。

Consider this example:考虑这个例子:

#include <stdio.h>
#include <inttypes.h>   

int main(void) {
    int32_t a = INT32_MIN;
    int32_t b = -1;
    int64_t c = a / b;
    printf("%" PRId64, c);
}

The result of division of INT32_MIN / -1 is representable in c , it is INT32_MAX + 1 , which is positive . INT32_MIN / -1的除法结果用c表示, INT32_MAX + 1 However on 32-bit platforms the arithmetic happens in 32 bits, and this division produces an integer overflow which causes the behaviour to be undefined.然而,在 32 位平台上,算术以 32 位进行,并且这种除法会产生整数溢出,从而导致行为未定义。 What happens on my computer is that if I compile without optimizations it aborts the program.在我的计算机上发生的情况是,如果我在没有优化的情况下进行编译,它会中止程序。 If I compile with optimizations enabled ( -O3 ), the compiler will resolve this calculation at compilation time, and handles the overflow in a peculiar way and produces the result -2147483648 which is negative .如果我在启用优化 ( -O3 ) 的情况下进行编译,编译器将在编译时解析此计算,并以特殊方式处理溢出并生成结果-2147483648负值

Likewise, if you do this:同样,如果你这样做:

uint16_t a = 16;
int16_t b = -1;
int32_t result = a / b;
printf("%" PRId32 "\n", result);

the result on a 32-bit int machine is -16.在 32 位 int 机器上的结果是 -16。 If you change the type of a to uint32_t the math happens in unsigned:如果更改的类型auint32_t数学发生在无符号:

uint32_t a = 16;
int16_t b = -1;
int32_t result = a / b;
printf("%" PRId32 "\n", result);

The result is of course 0 .结果当然是0 And you would get 0 from the former calculation too on a 16-bit machine.在 16 位机器上,你也会从前面的计算中得到0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM