pow和round-answers的问题并不等同

时间:2014-04-22 23:00:03

标签: c++ floating-point rounding pow

我在创建一个检查root是否可以简化的函数时遇到问题。在这个例子中,我试图简化108的立方根,并且第一个应该适用的数字是27。

为了做到这一点,我正在调用pow(),其中数字是索引(在本例中为27),功率为(1 / power),在本例中为3.我然后将其与pow的舍入答案(index,(1 / power))进行比较,也应为3。

包括我的问题的图片,但基本上,我得到两个相当于3的答案,但我的程序并不认为它们是平等的。它似乎在我的程序中的其他地方工作,但在这里不起作用。有什么建议吗?

int inside = insideVal;
int currentIndex = index;
int coeff = co;
double insideDbl = pow(index, (1/(double)power));
double indexDbl = round(pow(index,(1/(double)power)));
cout<<insideDbl<< " " << indexDbl <<endl;
//double newPow = (1/(double)power);
vector<int> storedInts = storeNum;
if(insideDbl == indexDbl){
    if(inside % currentIndex == 0){
        storedInts.push_back(currentIndex);
        return rootNumerator(inside/currentIndex, currentIndex, coeff, power, storedInts);
    }
    else{
        return rootNumerator(inside, currentIndex + 1, coeff, power, storedInts);
    }
}
else if(currentIndex < inside){
    return rootNumerator(inside, currentIndex + 1, coeff, power, storedInts);
}

我试图添加一张图片,但我的声誉显然不够高。在我的控制台中,我为阅读cout<<insideDbl<< " " << indexDbl <<endl;

的行获得“3 3”

编辑:

好吧,如果答案不准确,为什么同一类型的代码在我的程序中的其他地方工作?取16的第4个根(应该等于2)可以使用这段代码:

    else if( pow(initialNumber, (1/initialPower)) == round(pow(initialNumber,(1/initialPower)))){
        int simplifiedNum = pow(initialNumber, (1/initialPower));
        cout<<simplifiedNum;
        Value* simplifiedVal = new RationalNumber(simplifiedNum);
        return simplifiedVal;
    }
尽管条件与我遇到麻烦的情况完全相同,但仍然存在。

2 个答案:

答案 0 :(得分:0)

我怀疑你会通过计算insideVal的素数因子分解并获取出现在多个根中的素数的乘积来做得更好。

例如

108 = 2 2 ×3 3

因此

3 √108= 3× 3 √2 2

324 = 2 2 ×3 4

因此

3 √324= 3× 3 √(2 2 ×3)

您可以使用trial division来构建因子分解。

编辑 C ++实施

首先我们需要pow

的整数重载
unsigned long
pow(unsigned long x, unsigned long n)
{
    unsigned long p = 1;

    while(n!=0)
    {
        if(n%2!=0) p *= x;
        n /= 2;
        x *= x;
    }
    return p;
}

请注意,这只是适用于权力的peasant algorithm 接下来我们需要按顺序计算素数

unsigned long
next_prime(const std::vector<unsigned long> &primes)
{
    if(primes.empty()) return 2;

    unsigned long p = primes.back();
    unsigned long i;

    do
    {
        ++p;
        i = 0;
        while(i!=primes.size() && primes[i]*primes[i]<=p && p%primes[i]!=0) ++i;
    }
    while(i!=primes.size() && primes[i]*primes[i]<=p);

    return p;
}

请注意,primes预计包含的所有素数小于我们要查找的素数,并且一旦达到大于候选的平方根的素数,我们就可以退出检查{{因为那不可能是一个因素 使用这些函数,我们可以使用

计算我们可以在根外部获取的因子
p

将此应用于您的示例

unsigned long
factor(unsigned long x, unsigned long n)
{
    unsigned long f = 1;

    std::vector<unsigned long> primes;
    unsigned long p = next_prime(primes);

    while(pow(p, n)<=x)
    {
        unsigned long i = 0;
        while(x%p==0)
        {
            ++i;
            x /= p;
        }
        f *= pow(p, (i/n));

        primes.push_back(p);
        p = next_prime(primes);
    }
    return f;
}

给出了预期的结果。再举一个例子,试试

std::cout << factor(108, 3) << std::endl; //output: 3
您可以通过注明

来确认是否正确

3333960000 = 30 4 ×4116

并检查4116没有任何4的幂。

答案 1 :(得分:0)

你是有限精度浮点运算的受害者。

发生了什么事?

if(insideDbl == indexDbl)非常危险且具有误导性。事实上,这是一个问题(注意:我编写了确切的数字,但我可以给你精确的数字3.000000000000012552.999999999999996234相同。我放了14 0和14 9 s。从技术上讲,差异超出了15个最重要的地方。这很重要。

现在,如果您编写insideDbl == indexDbl,编译器会比较它们的二进制表示。哪个明显不同。但是,当您只是打印它们时,默认精度就像5或6位有效数字,因此它们会四舍五入,看起来是相同的。

如何检查?

尝试使用以下方式打印:

typedef std::numeric_limits< double > dbl_limits;
cout.precision(dbl::max_digits10);
cout << "Does " << insideDbl  << " == " << indexDbl << "?\n";

这将设置精度和位数,以区分两个数字。请注意,这高于保证的计算精度!这是混乱的根源。

我还鼓励阅读numeric_limits。特别是关于digits10max_digits10

为什么有时会有效?

因为有时候两种算法最终会使用相同的二进制表示形式来获得最终结果,有时它们会赢得。

同样2可能是一个特例,因为我相信它实际上可以用二进制形式表示。我认为(但我不知道如何)。所有2的幂(及其总和)都可以,如0,675 = 0,5+0,125 = 2^-1 + 2^-3。但除非有人确认,否则请不要理所当然。

你能做什么?

坚持精确的计算。使用整数或其他。或者你可以假设所有3.0 +/- 10^-10实际上都是3.0(epsilon比较),这至少可以说,当你关心精确的数学时,这是非常危险的。

Tl;博士:你永远无法比较两个floatdouble s的相等性,即使在数学上你可以证明所提到的相等性,因为有限的精度计算也就是说,除非您实际感兴趣的是值的相同二进制表示,而不是值本身。有时情况就是这样。