代码之家  ›  专栏  ›  技术社区  ›  Hari

除以2后的结果与乘以0.5后的结果之差

  •  2
  • Hari  · 技术社区  · 6 年前
    #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 回复  |  直到 6 年前
        1
  •  4
  •   Grant Sanders    6 年前

    假使 d ,则, 9999999999999999999 升级为double,如果您的C实现使用IEEE 754 double,将转换为100000000000000000(如果我的计算正确),因为有效位中只有53位可用,其中一位是隐含的1。100000000000000000乘以0.5等于500000000000000000。浮点很奇怪。请阅读 https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

        2
  •  2
  •   chqrlie    6 年前

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

    表达式 9999999999999999999U / 2 9999999999999999999U >> 1 因此,充分定义和评估 4999999999999999999 ,通常在编译时通过常量折叠,使用相同的类型。此值可以存储到 c e 并正确输出 printf 具有格式 %llu 正如所料。

    相反地 9999999999999999999U * 0.5 (或类似情况 9999999999999999999U / 2.0 )作为浮点表达式计算: (double)9999999999999999999U * 0.5 ,类型的浮点结果 double 已转换为 无符号长长整型 分配给时 d

    这个 双重的 类型只能保证提供足够的精度,以便在不丢失的情况下将数字转换为10位十进制数字,远远低于您的数字所需的精度。大多数C实现使用IEEE-754表示 双重的 精确到53位的类型。价值观 9999999999999999999 因此,四舍五入为 1E19 当转换为 双重的 。乘以 0.5 或除以 2.0 完全按照只更改二进制指数部分的方式执行。结果 5E18 已转换为 无符号长长整型 并打印为 5000000000000000000 正如您在系统上看到的那样。

        3
  •  0
  •   Ulrich Eckhardt    6 年前

    这些差异用类型传播来解释。

    第一个示例,将整数除以整数。除以2和右移在这里是等价的,它们按原样在操作数上进行。

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

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