用单精度浮点模拟双精度的运算,实现扩展浮点精度的算法

实际上,任何精度的浮点运算也可以像BigInteger那样计算,可以用多个浮点数据表示更高精度的浮点数据。而这就要求实现带余数的浮点四则运算

带余数的浮点四则运算的定义:

加法和减法,余数和结果的和能精确表示运算值。

乘法和除法,余数和结果必须同号,对于乘法,余数和结果的和能精确表示运算值,而对于除法,余数就是原来的意思。

 

不多解释,代码如下:(修复某些可能存在错误)

#include <iostream>
#include <xmmintrin.h>

using namespace std;


typedef struct {
    float hi;
    float lo;
} doublf;

/**
 * BigFloat的实际值为:
 * 使用这个数据类型的缺点是数据存储冗余,优点是便于计算。
**/
typedef struct {
    int32_t len;//data中有效数据的长度
    int32_t exp;//指数部分
    float data[0];//浮点数据,每个都是1.x的数据
} BigFloat;

inline int32_t ifabs(float a){
    int32_t p=*(int32_t*)&a;
    return p&0x7fffffff;
}

//o=5
doublf addf(float a,float b){//带余数加法
    doublf ret;
    ret.hi=a+b;
    if(ifabs(a)>ifabs(b))ret.lo=b-(ret.hi-a);
    else ret.lo=a-(ret.hi-b);
    return ret;
}

//o=5
doublf subf(float a,float b){//带余数减法
    doublf ret;
    ret.hi=a-b;
    if(ifabs(a)>ifabs(b))ret.lo=(a-ret.hi)-b;
    else ret.lo=a-(ret.hi+b);
    return ret;
}

//o=14
doublf mule(float a,float b){
    doublf ret;
    float m,n;//结合整数运算优化
    int32_t ap,bp,am,bm;
    uint32_t mp;
    ap=*(int32_t*)&a;
    bp=*(int32_t*)&b;
    ret.hi=n=a*b;
    am=ap&0xff800000;
    bm=bp&0xff800000;
    am=am+bm-0x4b000000;
    m=*(float*)&am;
    ap|=0x800000;
    bp|=0x800000;
    mp=(uint32_t)ap*(uint32_t)bp;
    ap=(mp&0x7fffff)|am;
    ret.lo=*(float*)&ap;
    if(mp&0x800000){
        if(n+m!=n)ret.lo-=m;
    }
    else ret.lo-=m;
    return ret;
}

//o=14
doublf mulf(float a,float b){//带余数乘法
    unsigned int csr=_mm_getcsr();
    _mm_setcsr((csr & ~_MM_ROUND_MASK) | _MM_ROUND_TOWARD_ZERO);
    doublf ret=mule(a,b);
    _mm_setcsr(csr);
    return ret;
}

//o=17
doublf divf(float a,float b){//带余数除法
    unsigned int csr=_mm_getcsr();
    _mm_setcsr((csr & ~_MM_ROUND_MASK) | _MM_ROUND_TOWARD_ZERO);
    doublf ret,tmp;
    ret.hi=a/b;
    tmp=mule(ret.hi,b);
    ret.lo=(a-tmp.hi)-tmp.lo;
    _mm_setcsr(csr);
    return ret;
}

//检验正确
//o=20,8
doublf add(doublf a,doublf b){//最後一位会产生误差
    doublf ret,tmp;
    /* 计算 */
    if(ifabs(a.hi)<ifabs(b.hi)){
        tmp=a;
        a=b;
        b=tmp;
    }
    ret=addf(b.lo,a.lo);
    tmp=addf(ret.hi,b.hi);
    ret.hi=tmp.hi;
    ret.lo+=tmp.lo;
    tmp=addf(tmp.hi,a.hi);
    ret.hi=tmp.hi;
    ret.lo+=tmp.lo;//即使高低位重叠也不会有影响
    return ret;
}

//检验正确
//o=22,9
doublf sub(doublf a,doublf b){//最後一位会产生误差
    doublf tmp;
    /* 计算 */
    tmp.hi=-b.hi;
    tmp.lo=-b.lo;
    return add(a,tmp);
}

//检验正确
doublf mul(doublf a,doublf b){
#if 0//最後一位会产生误差
//o=137,25
    unsigned int csr=_mm_getcsr();
    _mm_setcsr((csr & ~_MM_ROUND_MASK) | _MM_ROUND_TOWARD_ZERO);
    doublf ret,tmp,tmp2,tmp3,tmp4;
    /* 计算 */
    tmp=mule(a.lo,b.lo);
    tmp2=mule(a.lo,b.hi);
    tmp3=mule(a.hi,b.lo);
    tmp4=mule(a.hi,b.hi);
    _mm_setcsr(csr);
    ret=add(tmp,tmp2);//应允许误差存在
    ret=add(ret,tmp3);
    ret=add(ret,tmp4);
    return ret;
#else//最後两位会产生误差
//o=25,8
    doublf ret=mulf(a.hi,b.hi);
    ret.lo+=(a.lo*b.lo+a.lo*b.hi)+a.hi*b.lo;
    ret=addf(ret.hi,ret.lo);//克服高低位重叠造成的影响
    return ret;
#endif
}

//检验正确
doublf div(doublf a,doublf b){//最後两位会产生误差
#if 1//o=27,10
    doublf ret,tmp;
    float d=b.hi+b.lo;
    ret.hi=a.hi/d;
    unsigned int csr=_mm_getcsr();
    _mm_setcsr((csr & ~_MM_ROUND_MASK) | _MM_ROUND_TOWARD_ZERO);
    /* 计算 */
    tmp=mule(b.hi,ret.hi);
    //tmp2=mule(b.lo,ret.hi);
    _mm_setcsr(csr);
    ret.lo=((((a.hi-tmp.hi)-tmp.lo)-ret.hi*b.lo)+a.lo)/d;//精度低但更快
    //a=sub(a,tmp);
    //a=sub(a,tmp2);
    //ret.lo=a.hi/d;
    ret=addf(ret.hi,ret.lo);//还是应该修正,以克服高低位重叠的影响
    return ret;
#else//o=24,8
    doublf ret;
    float d=b.hi+b.lo;
    ret=divf(a.hi,d);
    ret.lo=(ret.lo-ret.hi*((d-b.hi)-b.lo)+a.lo)/d;
    return ret;
#endif
}

inline double to_double(doublf a){
    return (double)a.hi+a.lo;
}

inline doublf to_doublf(double a){
    doublf ret;
    ret.hi=a;
    ret.lo=a-ret.hi;
    return ret;
}

/**
 * 这样就能有办法用单精度模拟双精度(可以有2^-46的精度),甚至模拟任何精度的浮点数据。
 * 关键是要支持带余数的浮点四则运算。
**/

int main()
{
    int i;
    uint64_t tmp;
    doublf df,af,tf;
    double dd=45671.154545,ad=1.001245648454,td,tt;
    do{
        tf=df=to_doublf(dd);
        af=to_doublf(ad);
        cin>>i;
        while(i--){
            tt=to_double(df=add(df,af));
            tmp=*(uint64_t*)&tt;
            cout<<"add="<<hex<<tmp<<endl;
            td=dd+ad;
            tmp=*(uint64_t*)&td;
            cout<<"chk="<<hex<<tmp<<endl;
            dd=tt;
        };
        df=tf;
        dd=to_double(tf);
        cin>>i;
        while(i--){
            tt=to_double(df=sub(df,af));
            tmp=*(uint64_t*)&tt;
            cout<<"sub="<<hex<<tmp<<endl;
            td=dd-ad;
            tmp=*(uint64_t*)&td;
            cout<<"chk="<<hex<<tmp<<endl;
            dd=tt;
        };
        df=tf;
        dd=to_double(tf);
        cin>>i;
        while(i--){
            tt=to_double(df=mul(df,af));
            tmp=*(uint64_t*)&tt;
            cout<<"mul="<<hex<<tmp<<endl;
            td=dd*ad;
            tmp=*(uint64_t*)&td;
            cout<<"chk="<<hex<<tmp<<endl;
            dd=tt;
        };
        df=tf;
        dd=to_double(tf);
        cin>>i;
        while(i--){
            tt=to_double(df=div(df,af));
            tmp=*(uint64_t*)&tt;
            cout<<"div="<<hex<<tmp<<endl;
            td=dd/ad;
            tmp=*(uint64_t*)&td;
            cout<<"chk="<<hex<<tmp<<endl;
            dd=tt;
        };
        cin>>dd;
        cin>>ad;
    }while(ad!=0.0);
    return 0;
}

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值