【问题标题】:IEEE-754 double precision and splitting methodIEEE-754 双精度和拆分方法
【发布时间】:2016-05-25 15:11:07
【问题描述】:

当你计算初等函数时,你应用了不断的修改。特别是在 exp(x) 的实现中。在所有这些实现中,使用 ln(2) 的任何校正都分两步完成。 ln(2) 分为两个数:

static const double ln2p1   = 0.693145751953125;
static const double ln2p2   = 1.42860682030941723212E-6;
// then ln(2) = ln2p1 + ln2p2

那么使用 ln(2) 的任何计算都通过以下方式完成:

 blablabla -= ln2p1
 blablabla -= ln2p2

我知道这是为了避免舍入效应。但是为什么这两个数字特别在里面呢?你们中有些人知道如何获得这两个数字?

谢谢!

在第一条评论之后,我用更多的材料和一个非常奇怪的问题完成了这篇文章。我与我的团队一起工作,我们同意通过将数字 ln(2) 分成两个数字来潜在地使精度翻倍。为此,应用了两个转换,第一个:

1) c_h = floor(2^k ln(2))/2^k
2) c_l = ln(2) - c_h

k 表示精度,在 Cephes 库 (~1980) 中,float k 已固定为 9、16 为 double 和 16 为 long long double(为什么我不知道)。因此对于 double c_h 的精度为 16 位,而对于 c_l 的精度为 52 位。

据此,我编写了以下程序,并以 52 位精度确定 c_h。

 #include <iostream>
 #include <math.h>
 #include <iomanip>

 enum precision { nine = 9, sixteen = 16, fiftytwo = 52 };

 int64_t k_helper(double x){
     return floor(x/log(2));
 }

 template<class C>
 double z_helper(double x, int64_t k){
     x -= k*C::c_h;
     x -= k*C::c_l;
     return x;
 }

 template<precision p>
 struct coeff{};

 template<>
 struct coeff<nine>{
     constexpr const static double c_h = 0.693359375;
     constexpr const static double c_l = -2.12194440e-4;
 };

 template<>
 struct coeff<sixteen>{
     constexpr const static double c_h = 6.93145751953125E-1;
     constexpr const static double c_l = 1.42860682030941723212E-6;
 };

 template<>
 struct coeff<fiftytwo>{
     constexpr const static double c_h = 0.6931471805599453972490664455108344554901123046875;
     constexpr const static double c_l = -8.78318343240526578874146121703272447458793199905066E-17;
 };


 int main(int argc, const char * argv[]) {

    double x = atof(argv[1]);
    int64_t k = k_helper(x);

    double z_9  = z_helper<coeff<nine> >(x,k);
    double z_16 = z_helper<coeff<sixteen> >(x,k);
    double z_52 = z_helper<coeff<fiftytwo> >(x,k);


    std::cout << std::setprecision(16) << " 9  bits precisions " << z_9 << "\n"
                                       << " 16 bits precisions " << z_16 << "\n"
                                       << " 52 bits precisions " << z_52 << "\n";



    return 0;

}

如果我现在计算一组不同的值,我会得到:

bash-3.2$ g++ -std=c++11 main.cpp  
bash-3.2$ ./a.out 1
9  bits precisions 0.30685281944
16 bits precisions 0.3068528194400547
52 bits precisions 0.3068528194400547
bash-3.2$ ./a.out 2
9  bits precisions 0.61370563888
16 bits precisions 0.6137056388801094
52 bits precisions 0.6137056388801094
bash-3.2$ ./a.out 100
9  bits precisions 0.18680599936
16 bits precisions 0.1868059993678755
52 bits precisions 0.1868059993678755
bash-3.2$ ./a.out 200
9  bits precisions 0.37361199872
16 bits precisions 0.3736119987357509
52 bits precisions 0.3736119987357509
bash-3.2$ ./a.out 300
9  bits precisions 0.56041799808
16 bits precisions 0.5604179981036264
52 bits precisions 0.5604179981036548
bash-3.2$ ./a.out 400
9  bits precisions 0.05407681688
16 bits precisions 0.05407681691155647
52 bits precisions 0.05407681691155469
bash-3.2$ ./a.out 500
9  bits precisions 0.24088281624
16 bits precisions 0.2408828162794319
52 bits precisions 0.2408828162794586
bash-3.2$ ./a.out 600
9  bits precisions 0.4276888156
16 bits precisions 0.4276888156473074
52 bits precisions 0.4276888156473056
bash-3.2$ ./a.out 700
9  bits precisions 0.61449481496
16 bits precisions 0.6144948150151828
52 bits precisions 0.6144948150151526

就像当 x 大于 300 时会出现差异。我看了一下gnulibc的实现

http://osxr.org:8080/glibc/source/sysdeps/ieee754/ldbl-128/s_expm1l.c

目前它对 c_h 使用 16 位预置(第 84 行)

嗯,我可能遗漏了一些 IEEE 标准,我无法想象 glibc 中的精度错误。你怎么看?

最好的,

【问题讨论】:

    标签: precision inline-assembly ieee-754 exp elementary-functions


    【解决方案1】:

    ln2p1 正好是 45426/65536。这可以通过round(65536 * ln(2)) 获得。 ln2p2 只是余数。那么这两个数的特别之处在于分母65536(216)。

    我发现大多数使用这个常量的算法都可以追溯到 cephes 库,该库于 1984 年首次发布,当时 16 位计算仍然占主导地位,这可能解释了为什么 216 被选中。

    【讨论】:

    • hum 有趣的评论,至少我有一个历史答案的开头,它并没有真正回答我的问题,我希望在“每个计算机科学家应该知道的浮点知识”论文中找到一个补充答案number”,这 2^16 可能有充分的理由......继续
    猜你喜欢
    • 2013-07-24
    • 2014-05-28
    • 1970-01-01
    • 2011-11-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多