pow(a/b,x)
和pow(b/a,-x)
之间的准确性是否存在差异?
如果存在,将小于1的数字提高为正数或将大于1的数字提高为负数会产生更准确的结果吗?
编辑:假设使用x86_64处理器和gcc编译器。
编辑:我尝试使用一些随机数进行比较。例如:
printf("%.20f",pow(8.72138221/1.761329479,-1.51231)) // 0.08898783049228660424
printf("%.20f",pow(1.761329479/8.72138221, 1.51231)) // 0.08898783049228659037
因此,看起来似乎存在差异(尽管在这种情况下很小),但是也许知道算法实现的人可以评论最大差异是什么,在什么条件下。
答案 0 :(得分:2)
这是回答此类问题的一种方法,以查看浮点的行为方式。这不是分析此类问题的100%正确方法,但它给出了一个总体思路。
让我们生成随机数。计算浮点精度的v0=pow(a/b, n)
和v1=pow(b/a, -n)
。并以双精度计算ref=pow(a/b, n)
,并将其舍入为浮点数。我们使用ref
作为参考值(我们假设double的精度比float精度高得多,因此我们可以相信ref
被认为是可能的最佳值。对于大多数IEEE-754而言,这都是正确的的时间)。然后求和v0-ref
和v1-ref
之间的差。差异应使用“ v和ref之间的浮点数的数量”来计算。
请注意,结果可能取决于a
,b
和n
的范围(以及随机生成器的质量。如果确实很差,则可能会产生偏差)结果)。在这里,我使用了a=[0..1]
,b=[0..1]
和n=[-2..2]
。此外,该答案假设浮点/双除法/功率的算法是相同的,具有相同的特征。
对于我的计算机,总和的差异为:2604828 2603684
,这意味着两者之间没有显着的精度差异。
这是代码(请注意,此代码假设IEEE-754算术):
#include <cmath>
#include <stdio.h>
#include <string.h>
long long int diff(float a, float b) {
unsigned int ai, bi;
memcpy(&ai, &a, 4);
memcpy(&bi, &b, 4);
long long int diff = (long long int)ai - bi;
if (diff<0) diff = -diff;
return diff;
}
int main() {
long long int e0 = 0;
long long int e1 = 0;
for (int i=0; i<10000000; i++) {
float a = 1.0f*rand()/RAND_MAX;
float b = 1.0f*rand()/RAND_MAX;
float n = 4.0f*rand()/RAND_MAX - 2.0f;
if (a==0||b==0) continue;
float v0 = std::pow(a/b, n);
float v1 = std::pow(b/a, -n);
float ref = std::pow((double)a/b, n);
e0 += diff(ref, v0);
e1 += diff(ref, v1);
}
printf("%lld %lld\n", e0, e1);
}
答案 1 :(得分:2)
通常,具有正幂的形式稍好一些,尽管过少可能不会产生实际效果。具体情况可以区分。例如,如果 a 或 b 是2的幂,则应将其用作分母,因为除法将没有舍入误差。
在这个答案中,我假设IEEE-754二进制浮点的舍入舍入为偶数,并且涉及的值在浮点格式的正常范围内。
给a
,b
和x
赋值 a , b 和 x ,以及pow
的一种实现,它计算出最接近理想数学值的可表示值(实际实现通常不是那么好),pow(a/b, x)
会计算( a / b •(1+ e 0 )) x •(1+ e 1 ),其中 e 0 是除法中出现的舍入误差,而 e < sub> 1 是pow
中发生的舍入误差,pow(b/a, -x)
计算( b / a •(1+ e 2 )))- x •(1+ e 3 ),其中 e 2 和 e 3 是该除法的舍入误差,而{ {1}}。
每个错误 e 0 ... e 3 位于区间[− u / 2, u / 2],其中 u 是浮点格式的最低精度(ULP)单位1。 (符号[ p , q ]是包含从 p 到 q 的所有值(包括 > p 和 q 。)如果结果位于binade的边缘附近(浮点指数发生变化,并且有效数接近于1),则下界可能是- u / 4。目前,我不会分析这种情况。
重写,它们是( a / b ) x •(1+ e < / em> 0 ) x •(1+ e 1 )和( a / b ) x •(1+ e 2 < / sub>)- x •(1+ e 3 )。这表明(1+ e 0 ) x 与(1+ e 2 )- x 。 1+ e 1 与1+ e 3 也是不同的,但这只是最后的舍入。 [我可能会在稍后考虑对此进行进一步分析,但现在忽略它。]
考虑(1+ e 0 ) x 和(1+ e 2 )- x 。第一个表达式的电位值跨度[[1- u / 2) x ,(1+ u / 2) x ],而第二个跨度[(1+ u / 2) − x ,(1− u / 2) - x ]。当 x > 0时,第二个间隔比第一个间隔长:
因此,在指数形式为正的形式上,其潜在结果的时间间隔较短的意义更好。
尽管如此,这种差异很小。如果在实践中无法观察到我,我不会感到惊讶。同样,人们可能会关注错误的概率分布,而不是潜在错误的范围。我怀疑这也会有利于正指数。
答案 2 :(得分:2)
...在
pow(a/b,x)
和pow(b/a,-x)
之间...将小于1的数字提高为正幂或将大于1的数字提高为负幂会产生更准确的结果吗?
以哪种划分更为弓形。
考虑z = x y = 2 y * log2(x)。
大约:y * log2(x)
中的错误被z
的值放大,从而形成z
中的错误。 x y 对x
中的错误非常敏感。 |log2(x)|
越大,关注越大。
在OP的情况下,pow(a/b,p)
和pow(b/a,-p)
通常都具有相同的y * log2(x)
和相同的z
,并且在z
中具有相似的错误。这是关于x, y
的形成方式的问题:
a/b
和b/a
通常都具有+/- 0.5 * unit in the last place的相同误差,因此两种方法的类似的错误。
然而,选择值分别为a/b
和b/a
的情况下,一个商将更为精确,并且该方法的误差较小,pow()
。
pow(7777777/4,-p)
比pow(4/7777777,p)
更准确。
由于缺乏对分割错误的保证,因此一般规则适用:无重大差异。
答案 3 :(得分:0)
对于像您这样的舍入错误评估,使用一些多精度库(例如Boost.Multiprecision)可能会很有用。然后,您可以比较各种精度的结果,例如,使用以下程序:
#include <iomanip>
#include <iostream>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
namespace mp = boost::multiprecision;
template <typename FLOAT>
void comp() {
FLOAT a = 8.72138221;
FLOAT b = 1.761329479;
FLOAT c = 1.51231;
FLOAT e = mp::pow(a / b, -c);
FLOAT f = mp::pow(b / a, c);
std::cout << std::fixed << std::setw(40) << std::setprecision(40) << e << std::endl;
std::cout << std::fixed << std::setw(40) << std::setprecision(40) << f << std::endl;
}
int main() {
std::cout << "Double: " << std::endl;
comp<mp::cpp_bin_float_double>();
td::cout << std::endl;
std::cout << "Double extended: " << std::endl;
comp<mp::cpp_bin_float_double_extended>();
std::cout << std::endl;
std::cout << "Quad: " << std::endl;
comp<mp::cpp_bin_float_quad>();
std::cout << std::endl;
std::cout << "Dec-100: " << std::endl;
comp<mp::cpp_dec_float_100>();
std::cout << std::endl;
}
在我的平台上,其输出为:
Double:
0.0889878304922865903670015086390776559711
0.0889878304922866181225771242679911665618
Double extended:
0.0889878304922865999079806265115166752366
0.0889878304922865999012043629334822725241
Quad:
0.0889878304922865999004910375213273866639
0.0889878304922865999004910375213273505527
Dec-100:
0.0889878304922865999004910375213273881004
0.0889878304922865999004910375213273881004
实时演示:https://wandbox.org/permlink/tAm4sBIoIuUy2lO6
对于double
,第一个计算更准确,但是,我猜这里不能得出任何一般性结论。
此外,请注意,您的输入数字不能使用IEEE 754双精度浮点类型(它们都不是)精确地表示。问题是,您是否关心使用最接近表示的确切数字进行计算的准确性。