2

我用这个编译器编译这段代码。对于数字,我写 18446744073709551615 (2^64-1)。Pelles 的可执行文件说“18446744073709551615 是素数”,但 GCC 的可执行文件说“18446744073709551615 不是素数”。为什么结果不一样?

#include <stdio.h>
#include <math.h>
int main(void)
{
    unsigned long long number;
    printf("number: ");
    scanf("%llu",&number);
    unsigned long trsq=truncl(sqrtl(number));
    char s=1;
    for(unsigned long i=2;i<=trsq;i++) {
        if (number%i==0) {
            s=0;
            break;
        }
    }
    if (s==1) {
        printf("%llu is prime\n",number);
    } else {
        printf("%llu isn't prime\n",number);
    }
    return 0;
}

编辑:

我已经测试过,gcc 给出 12,pelles c 给出 8 的 sizeof(long double)。

4

1 回答 1

2

许多事情在 C 语言的定义中都没有说明,包括数字类型的大小以及在溢出或丢失精度的情况下会发生什么。因此,当您不检查数字类型范围或使用浮点时,通常会通过不同的实现(不同的编译器、不同的硬件、不同的操作系统)得到不同的结果。

鉴于自己提供的信息两条 评论中,这是对正在发生的事情的合理解释。我没有 Pelles C 可以检查。

佩莱斯警告:

警告 #2215:从“unsigned long long int”转换为“long double”;可能的数据丢失。警告 #2215:从“long double”转换为“unsigned long int”;可能丢失数据

猜想 #1:数学值 2^64-1 不能精确地用 a 表示long double(这很可能,因为 2^64-1 需要 64 位尾数,而很少有实现有那么多)。四舍五入为2^64,可以精确表示。

的值为number2^64-1,它是一个unsigned long long. 由于该函数sqrtl需要一个long double参数,因此该值将转换为该类型。给定猜想 #1,sqrtl得到 2^64 的值。因此结果是 2^32。由于这是一个整数,truncl因此返回相同的值。

猜想 #2:unsigned long是 32 位类型(这在 32 位机器上几乎是常态,在 64 位版本的 Windows 上也是如此,至少使用 Microsoft 编译器)。

如果unsigned long是 32 位类型,则值 2^32 会溢出它。C 标准没有定义从浮点值到整数值的转换中溢出的情况,编译器可以选择做任何他们想做的事情。

猜想#3:在 Pelles C 中,当浮点值转换为整数类型时,它会以类型的大小为模进行包装,就像转换为较小的整数类型时发生的情况一样。

在猜想 #3 下,尝试将值 2^32 分配给32 位宽trsq的类型,将其设置为 0。因此值为 0,循环运行 0 次,程序错误地报告数字是素数。unsigned longtrsqfor

一个简单的解决方法是更改trsq​​为unsigned long long.

请注意,如果某些数字的最大素数因子非常接近它们的平方根(例如,如果数字是素数的平方),您的程序可能会将它们报告为素数,因为转换为number浮点值可能会将其舍入,所以trsq最终可能小于平方根,甚至小于小于平方根的最大整数。

您可以通过执行整数平方根计算来避免所有这些麻烦。

于 2014-01-31T02:23:06.993 回答