C unsigned long 转换不正确添加

C unsigned long with casting not adding correctly

我无法获得第三行打印的 15136704000 的正确值,我不确定问题出在哪里。通过 gccLinux 上编译时它可以正常工作,但是 Windows 一直在胡说八道,我只是想了解为什么。
Windows 显示为 Which is 2251802112 inches away.

#include <stdio.h>

int main(void)
{
    const int five = 5;
    const int eight = 8;
    const int mi_to_in = 63360;

    int miles_to_moon = 238900;
    int km_to_moon = (float) eight / five * miles_to_moon;
    unsigned long inches_to_moon = (long) miles_to_moon * mi_to_in;

    printf("The moon is %d miles away.\n", miles_to_moon);
    printf("Which is equivalent to %d kilometers away.\n", km_to_moon);
    printf("Which is %lu inches away.\n", inches_to_moon);
}

正如@jamesdlin 评论的那样,表达式 (long)miles_to_moon * mi_to_in 导致 Windows 上的算术溢出,因为类型 long 在该系统上只有 32 位,包括其 64 位版本。使用 unsigned long long 进行此计算可以解决问题,您实际上应该使用 long 进行 mi_to_in 并使用 miles_to_moon 进行某些系统的可移植性。

C 标准在支持它们的系统上提供固定长度的整数类型,例如 <stdin.h> 中定义的 int32_tint64_t。这些类型可用于具有适当范围的这些变量,但为了更好的可移植性和简单性,您应该使用 double 进行此类计算:

#include <stdio.h>

int main() {
    double mi_to_in = 63360;    /* exact figure */
    double mi_to_km = 1.60934;  /* exact figure */

    double miles_to_moon = 238900;  /* average distance approximation */
    double km_to_moon = miles_to_moon * mi_to_km;
    double inches_to_moon = miles_to_moon * mi_to_in;

    printf("The moon is %.0f miles away.\n", miles_to_moon);
    printf("Which is equivalent to %.0f kilometers away.\n", km_to_moon);
    printf("Which is %.0f inches away.\n", inches_to_moon);
    return 0;
}

输出:

The moon is 238900 miles away.
Which is equivalent to 384471 kilometers away.
Which is 15136704000 inches away.

但是请注意,将近似数字乘以精确数字并不会增加精度,上述输出中的有效位数可能表明了这一点。将这些数字四舍五入似乎更可取,但这将产生 384500 公里,这不是通常使用的数字 384400 公里。

更精确的平均值 semi-axis 是 384399 公里,大约 238855 英里,通常转换为 238900 英里。

四舍五入到指定数量的有效数字并不简单,C 库中没有标准函数可以做到这一点。您可以使用 snprintf%.3e 来生成指数格式的数字,然后使用 strtod 转换回来,但是很麻烦且效率低下。