如何在C语言中实现精准的FixedPoint定点数计算?
如何在C语言中实现精准的FixedPoint定点数计算?
我在C语言中使用struct来存储定点数的整数部分和小数部分。我猜测函数存在错误,可能未正确处理溢出,但不知道如何修改。
输入示例如下:
3.14123412345 + 0.23 5.6324 - 6.43252 8.112 * 1.31
我的输出结果为:
3.000000 -1.000000 0.000000
程序输出完全错误,数值的精度在存储或计算过程中丢失。
以下是我的初始代码:
#include<stdio.h> #include<string.h> #include<math.h> #define maxn 110000 #define uint unsigned int #define ull unsigned long long typedef struct { int intgr; uint frac; }PointFixedNum; PointFixedNum a,b,ans; char s1[maxn],s2[maxn],op; double num1=0.0,num2=0.0; void init(PointFixedNum *a,double num) { a->intgr=(int)num; a->frac=(uint)((ull)(num-a->intgr)<<32); } void clear() { a.frac=b.frac=ans.frac=0; a.frac=b.frac=ans.frac=0; num1=0.0; num2=0.0; } void read() { scanf("%1100[^ ]",s1); scanf(" %c",&op); scanf("%1100s",s2); sscanf(s1,"%lf",&num1); sscanf(s2,"%lf",&num2); } PointFixedNum add(PointFixedNum a,PointFixedNum b) { PointFixedNum c; ull sum=(ull)a.frac+b.frac; c.frac=(uint)(sum&((1ULL<<32)-1)); c.intgr=a.intgr+b.intgr+(int)(sum>>32); return c; } PointFixedNum sub(PointFixedNum a,PointFixedNum b) { PointFixedNum c; ull diff=(ull)a.frac-b.frac; c.frac=(uint)(diff&((1ULL<<32)-1)); c.intgr=a.intgr-b.intgr-(int)(diff>>32); } PointFixedNum mul(PointFixedNum a,PointFixedNum b) { PointFixedNum c; ull pro1=(ull)a.intgr*b.intgr; ull pro2=(ull)a.frac*b.frac; ull temp=(ull)((ull)pro1<<32)+(ull)pro2; c.intgr=(int)(temp>>32); c.frac=(uint)(temp&((1ULL<<32)-1)); } void print(PointFixedNum a) { double fraction=(double)a.frac/(1ULL<<32); double fin=(double)a.intgr+fraction; printf("%lf",fin); } int main() { read(); init(&a,num1); init(&b,num2); if(op=='+') { ans=add(a,b); } else if(op=='-') { ans=sub(a,b); } else if(op=='*') { ans=mul(a,b); } print(ans); return 0; }
更新内容:
我根据评论和回答修复了程序中的部分bug,结果如下:
输入示例
3.14123412345 + 0.23 5.6324 - 6.43252 8.112 * 1.31
输出结果
3.37123412336 -0.80012000003 10.62671999843
目前各函数功能已实现,至少逻辑上无问题。当前仅存的问题是精度:加减运算在十位小数后会丢失精度。
如何提升精度?
有没有方法可以提升精度?或者十位小数后的精度损失是不可避免的?
如果确实不可避免,由于加减运算仅涉及位数最短的数,是否有办法在输出中保留未变化的部分?
示例:3.14123412345 + 0.23 = 3.37123412345,小数点后前两位(3.14)与0.23相加,但123412345部分未变化,能否直接在输出中保留这部分?
关于乘法运算
当输出结果的小数位数有限时,结果相当准确,有没有办法将输出的小数位数限制为与实际结果一致?
示例:8.112 * 1.31 = 10.62672,若能将输出小数位数限制为5位,结果就是正确的。
以下是我调整后的代码:
#include<stdio.h> #include<string.h> #include<math.h> #include<stdint.h> #define maxn 110000 #define int int32_t #define uint uint32_t #define ull uint64_t typedef struct { int intgr; uint frac; }PointFixedNum; PointFixedNum a,b,ans; const ull scale=(1ULL<<32); char s1[maxn],s2[maxn],op; double num1=0.0,num2=0.0; void init(PointFixedNum *a,double num) { a->intgr=(int)num; a->frac=(uint)((num-a->intgr)*scale); } /*void init(PointFixedNum *a, double num) { static const long long scale = 1LL << 32; // Round to get y long long y = llround(num*scale); // Now break y into 2 parts: a->intgr = y/scale; int32_t frac = y%scale; if (frac >= 0) { a->frac = frac; } else { a->frac = scale + frac; a->intgr--; } }*/ // I check this version and the result is the same. void clear() { a.frac=b.frac=ans.frac=0; a.frac=b.frac=ans.frac=0; num1=0.0; num2=0.0; } void read() { scanf(" %1100[^ ]",s1); scanf(" %c",&op); scanf("%1100s",s2); sscanf(s1,"%lf",&num1); sscanf(s2,"%lf",&num2); } PointFixedNum add(PointFixedNum a,PointFixedNum b) { PointFixedNum c; ull sum=(ull)a.frac+b.frac; c.frac=(uint)(sum%scale); c.intgr=a.intgr+b.intgr+(int)(sum/scale); return c; } PointFixedNum sub(PointFixedNum a,PointFixedNum b) { PointFixedNum c; ull diff=(ull)a.frac-b.frac; c.frac=(uint)(diff%scale); c.intgr=a.intgr-b.intgr-(int)(diff/scale); return c; } PointFixedNum mul(PointFixedNum a,PointFixedNum b) { PointFixedNum c={0,0}; //ull scale=1ULL<<32; ull pro1=(ull)a.intgr*b.intgr; ull pro2=(ull)a.intgr*b.frac; ull pro3=(ull)a.frac*b.intgr; ull pro4=(ull)a.frac*b.frac; ull carry=pro4%scale>=scale/2; ull sum=pro4/scale+pro3%scale+pro2%scale+carry; c.frac=sum%scale; carry=sum/scale; sum=pro1%scale+pro2/scale+pro3/scale+carry; c.intgr=sum%scale; carry=sum/scale; sum=pro1/scale+carry; return c; } void print(PointFixedNum a) { double fraction=(double)a.frac/scale; double fin=(double)a.intgr+fraction; printf("%.11lf",fin); } int main() { read(); init(&a,num1); init(&b,num2); if(op=='+') { ans=add(a,b); } else if(op=='-') { ans=sub(a,b); } else if(op=='*') { ans=mul(a,b); } print(ans); return 0; }
内容的提问来源于stack exchange,提问作者FriedrichC
相关产品推荐
相关产品推荐

