除以2并乘以0.5后的结果之间的差异

时间:2018-03-10 08:23:27

标签: c precision

#include <stdio.h>

int main() {
    unsigned long long int c = 9999999999999999999U / 2;
    unsigned long long int d = 9999999999999999999U * 0.5;
    unsigned long long int e = 9999999999999999999U >> 1;
    printf("%llu\n%llu\n%llu\n", c, d, e);
    return 0;
}

所以输出是:

4999999999999999999
5000000000000000000
4999999999999999999

为什么乘以0.5会有区别? 当数字很小时,为什么不会出现这种差异呢?

3 个答案:

答案 0 :(得分:4)

d的情况下,9999999999999999999被提升为double,如果您的C实现使用IEEE 754双打,则会转换为10000000000000000000(如果我正确地进行了计算),因为它们只是在有效数字中有53位可用,其中一个是隐含的1.乘以10000000000000000000乘0.5是5000000000000000000.浮点很奇怪。请在https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html处阅读。

答案 1 :(得分:2)

9999999999999999999U是一个很大的数字。它需要64位来表示二进制。 C标准保证类型unsigned long long int具有至少64个值位,因此根据较小整数类型的实际范围,它是类型为unsigned intunsigned long int的整数常量或最多unsigned long long int

表达式9999999999999999999U / 29999999999999999999U >> 1因此被完全定义并评估为4999999999999999999,通常在编译时通过常量折叠,具有相同的类型。此值可以存储到ce中,并按printf正确输出,格式为%llu

相反,9999999999999999999U * 0.5(或类似9999999999999999999U / 2.0)被评估为浮点表达式:(double)9999999999999999999U * 0.5,类型double的浮点结果转换为unsigned long long int分配给d时。

double类型仅保证提供足够的精度以转换最多10位十进制数字而不会丢失,比您的号码所需要的少得多。大多数C实现使用具有精确53位精度的double类型的IEEE-754表示。因此,当转换为9999999999999999999时,值1E19将舍入为double。乘以0.5或除以2.0的方式与仅更改二进制指数部分完全相同。结果5E18将转换为unsigned long long int并打印为5000000000000000000,如您在系统中看到的那样。

答案 2 :(得分:0)

差异用类型传播来解释。

第一个例子,将整数除以整数。除以2和右移相当于此,它们是按原样在操作数上完成的。

第二个例子,将整数除以double。在这里,编译器将首先将整数操作数转换为double(我认为只保证十位十进制数),然后执行除法。为了再次将结果存储为整数,它将被截断。

我希望这说明不同类型的操作数会导致不同的操作,即使它们从数学的角度来看似乎是相似的。