<math.h> pow()给出了错误的结果</math.h>

时间:2014-04-11 15:18:52

标签: c

这是来自谷歌的代码堵塞,练习问题&#34;所有你的基地&#34;。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
long long pow_longlong(int digit, int raiseto)
{
    if (raiseto == 0) return 1;
    else return digit * pow_longlong(digit, raiseto - 1);
}
long long base10_with_map(int base, char* instr, char* digits)
{
    if (base < 2) base = 2;
    long long result = 0;
    int len = strlen(instr);
    int i = 0;
    while (len--)
        result += digits[instr[len]] * pow_longlong(base, i++);
    return result;
}
long long test(char* in)
{
    char appear[256];
    int i;
    int len = strlen(in);
    int hold = 0;
    for (i = 0; i < 256; i++) appear[i] = '\xFF';
    for (i = 0; i < len; i++)
        if (appear[in[i]] == '\xFF')
        {
            if (hold == 0) { appear[in[i]] = 1; hold++; }
            else if (hold == 1) { appear[in[i]] = 0; hold++; }
            else appear[in[i]] = hold++;
        }
    return base10_with_map(hold, in, appear);
}
int main(int argc, char* argv[])
{
    if (argc < 2)
    {
        printf("Usage: %s <input-file> \n", argv[0]); return 1;
    }
    char buf[100];
    int a, i;
    FILE* f = fopen(argv[1], "r");
    fscanf(f, "%d", &a);
    long long result;
    for (i = 1; i <= a; i++)
    {
        fscanf(f, "%s", buf);
        result = test(buf);
        printf("Case #%d: %lld\n", i, result);
    }
    return 0;
}

这可以按预期工作,并为问题产生正确的结果。但是如果我用math.h中的pow()替换自己的pow_longlong(),那么一些计算会有所不同。

这是什么原因?好奇。

编辑: - 没有溢出,普通长度足以存储值,长期只是矫枉过正 - 当然我包括math.h - 例如:test(&#34; wontyouplaywithme&#34;),pow_longlong返回674293938766347782(右)和math.h 674293938766347904(错误)

2 个答案:

答案 0 :(得分:0)

pow(请参阅reference)未定义整数,但仅针对浮点数。如果您以pow作为参数致电int,则结果将为double

您通常不能假设pow的结果与使用纯函数pow_longlong中的纯整数数学完全相同。

来自维基百科的关于double precision floating point numbers的引用:

  

在2 ^ 52 = 4,503,599,627,370,496和2 ^ 53 = 9,007,199,254,740,992之间   可表示的数字正是整数。对于下一个范围,   从2 ^ 53到2 ^ 54,一切都乘以2,所以可以表示   数字是偶数等等。

如果结果大于2 ^ 53,那么pow会得到不准确的结果。

答案 1 :(得分:0)

很抱歉,我不会通过你的例子和你的中间功能;由于double不足而导致您遇到的问题,而不是long long。只是数字变得太大,导致它需要越来越精确到最后,超过double可以安全地代表。

在这里,尝试这个非常简单的程序,或者只是信任我附加的输出,看看我的意思:

#include <stdio.h>

int main( ){

    double a;
    long long b;

    a = 674293938766347782.0;
    b = a;

    printf( "%f\n", a );
    printf( "%lld", b );

    getchar( );
    return 0;
}

/*
    Output:
    674293938766347780.000000
    674293938766347776
*/

你看,double可能有8个字节,与long long一样多,但它的设计使得它也能够保存非整数值,这使得在某些情况下,比long long更不精确。

我不知道确切的具体细节,但在这里,in MSDN据说它的表示范围是从-1.7e308+1.7e308(可能只是平均)15位精度

因此,如果您只打算使用正整数,请坚持使用您的函数。如果您想拥有优化版本,请查看以下内容:https://stackoverflow.com/a/101613/2736228

它利用了这样一个事实:例如,在计算x to the power 8时,你可以逃脱3次操作:

...
    result = x * x;             // x^2
    result = result * result;   // (x^2)^2 = x^4
    result = result * result;   // (x^4)^2 = x^8
...

而不是处理7个操作,将它们逐个相乘。