在导线平差中,对于double型数据,在计算过程中存在数据的丢失,会导致结果不准确。而平差对于精度的要求比较高,在工程应用中,这也是一个亟需解决的问题。本文研究是为了解决double型的加减运算时的精度无法控制的问题,按照本文所设计的算法可以使计算结果可以精确到小数点后6位。
1. 基本思路
分别写一个加法和减法的方法,将两个double型的数据的每一位一一取出,放在数组中,再对两数相应的位一一进行加减运算,最后将计算的结果数组转化为double型的数据返回。
2. 具体实现
2.1 读取数据
对于两个double型的数据,先将其每一位一一取出。对于整数部分,可采用“/”取整运算符除以10、100或1000等得到。
例如:若有数据double n=123.456, 定义存放整数部分数据的数组为 int integer_n[100],则其第i位的数为integer_n[i]= n/pow(10,i),这一语句只能取出数据的最高位。但还需要再设一个辅助变量数组double oddinteger_n[100],而oddinteger_n[i]= (oddinteger_n[i+1]-integer_n[i+1]*(pow(10,i+1))),对于取数据的非最高位,integer_n[i]=oddinteger_n[i]/pow(10,i),表示若取出最高位1后,oddinteger_n[i]的值为23.456,此时integer_n[i]的值为2,依次进行下去。
但是,对于小数部分,需要另外写一个循环,比较麻烦。因此,可以将n转化为一个小数部分为0的数,在一个循环内将其数据全部取出。即n=n*pow(10,6)(n的小数部分有6位)。此过程的具体代码如下(包含整个方法所有的变量的定义):
int integer_n[100]; //n的整数部分
int integer_m[100]; //m的整数部分
double oddinteger_n[100]; //n的整数部分辅助变量
double oddinteger_m[100]; //m的整数部分辅助变量
int addalone[100]; //每位的和
double add[100]; //求和辅助变量
double sum=0.0; //最终结果
n=n*pow(10,6); //将n化为整数
m=m*pow(10,6); //将m化为整数
for(int i=15;i>=0;i--) //将n、m的整数部分一一取出
{
if(i==15) //最高位单独取出
{
integer_n[i]=n/pow(10,i);
oddinteger_n[i]=n;
integer_m[i]=m/pow(10,i);
oddinteger_m[i]=m;
}
else //取出其它位
{
oddinteger_n[i]= (oddinteger_n[i+1]-integer_n[i+1]*(pow(10,i+1)));
integer_n[i]=oddinteger_n[i]/pow(10,i);
oddinteger_m[i]= (oddinteger_m[i+1]-integer_m[i+1]*(pow(10,i+1)));
integer_m[i]=oddinteger_m[i]/pow(10,i);
}
2.2 加法的实现
实现加法运算时,将两数的每一位一一相加,从最低位开始。计算加法时,需要考虑进位的问题。当相加的和大于9时,就需向前进一位,即循环体中下一次求和时还需加进位数,且当前数据需减10。这一过程比较繁琐,需要在两个循环体中进行。具体代码如下:
for(int j=0;j<=15;j++) //将n与m的每一位相加
{
if(j==0) //最低位相加
{
addalone[0]=integer_n[0]+integer_m[0];
}
else //其它位相加
{
if(addalone[j-1]>9) //前一次求和大于9时,此次需加1
{
addalone[j]=integer_n[j]+integer_m[j]+1;
}
else //无进位时正常求和
{
addalone[j]=integer_n[j]+integer_m[j];
}
}
}
for(int j=0;j<=15;j++) //求出最后的和
{
if(addalone[j]>9) //若有进位,则需减10
{
addalone[j]-=10;
}
//将每一位求和的数组整体转换为一个double型的数据
if(j==0)
{
add[j]=addalone[j];
}
else
{
add[j]=add[j-1]+addalone[j]*(pow(10,j));
}
}
sum=add[15]*pow(10,-6); //add[15]即为此次结果,将其还原为有6位小数的数据,即为最终的求和结果
return sum; //为了获取到求和的结果,最后需返回“sum”
2.3 减法的实现
实现减法运算时,将两数的每一位一一相减,从最低位开始。计算减法时,需要考虑借位的问题。而且由于减法有正负的问题,需要比较两数的大小后分两种情况考虑。具体代码如下:
if(n>=m) //n比m大时
{
for(int j=0;j<=15;j++) //将n与m的每一位相减
{
if(integer_n[j]>=integer_m[j]) //如果n的当前位大于m,正常相减
{
addalone[j]=integer_n[j]-integer_m[j];
}
else //n的当前位小于m,则需借位,且被借位要减1
{
addalone[j]=integer_n[j]-integer_m[j]+10;
integer_n[j+1]=integer_n[j+1]-1;
}
}
}
if(n<m) //n比m小时,用m 减n,再在所求得的数前加“-”
{
for(int j=0;j<=15;j++) //将m与n的每一位相减
{
if(integer_m[j]>=integer_n[j]) //如果m的当前位大于n,正常相减
{
addalone[j]=integer_m[j]-integer_n[j];
}
else //m的当前位小于n,则需借位,且被借位要减1
{
addalone[j]=integer_m[j]-integer_n[j]+10;
integer_m[j+1]=integer_m[j+1]-1;
}
}
}
for(int j=0;j<=15;j++) //求出最后的和(与加法运算时的基本相同)
{
//if(addalone[j]>9)
//{
// addalone[j]-=10;
//}
if(j==0)
{
add[j]=addalone[j];
}
else
{
add[j]=add[j-1]+addalone[j]*(pow(10,j));
}
}
//将结果还原为有6位小数的数据,即为最终的求差结果
if (n>=m) //
{
sum=add[15]*pow(10,-6);
}
if (n<m)
{
sum=-add[15]*pow(10,-6); //若n小于m,在最终结果前加“-”
}
return sum; //为了获取到求和的结果,最后需返回“sum”
3. 结论
本文解决了全站仪导线平差中的加减法中数据丢失的问题,对精度的控制也有了一定的提高。但是仍存在着一些问题,如只能精确到小数点后6位,没有解决乘除的精度控制等,这将会在以后的工作中进行改进。