#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
会有区别?
当数字很小时,为什么不会出现这种差异呢?
答案 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 int
,unsigned long int
的整数常量或最多unsigned long long int
。
表达式9999999999999999999U / 2
和9999999999999999999U >> 1
因此被完全定义并评估为4999999999999999999
,通常在编译时通过常量折叠,具有相同的类型。此值可以存储到c
和e
中,并按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(我认为只保证十位十进制数),然后执行除法。为了再次将结果存储为整数,它将被截断。
我希望这说明不同类型的操作数会导致不同的操作,即使它们从数学的角度来看似乎是相似的。