普通的实数求和函数:
template< class DATA_TYPE >
DATA_TYPE Sum( const DATA_TYPE A[], int n )
{
DATA_TYPE sum = 0.0;
for (int i=0; i<n; i++)
sum += A[i];
return sum;
}
修正一下中间计算产生的误差:
template< class DATA_TYPE >
DATA_TYPE KahanSum( const DATA_TYPE A[], int n )
{
DATA_TYPE sum=0, C=0, Y, T;
for (int i=0; i<n; i++)
{
Y = A[i] - C; // 修正误差
T = sum + Y;
C = T - sum - Y; // 计算产生的误差
sum = T;
}
return sum;
}
将上面两个函数的计算结果与一个不使用中间变量暂存的汇编函数比较:
double SumDouble( const double A[], int n )
{
double sum = 0;
__asm
{
mov ebx, A
mov ecx, n
fld sum
next:
fadd qword ptr [ebx]
add ebx, 8
sub ecx, 1
jnz next
fst sum
}
return sum;
}
试验数据和调用过程:
#define TYPE float
int main(int argc, char* argv[])
{
TYPE A[100];
_controlfp( _PC_24, _MCW_PC );
srand( time( NULL ) );
for( int i=0; i<100; i++ )
A[i] = (TYPE)rand()/RAND_MAX;
printf( "Last:\n\t%25.18f\n\t%25.18f\n\t%25.18f\n",
SumFloat( A, 100 ), KahanSum( A, 100 ), Sum(A,100) );
return 0;
}
试验结果:
float类型
函数
_PC_24
_PC_53
_PC_64
普通求和Sum
49.286575317382813000
52.753040313720703000
49.584823608398437000
误差修正KanhanSum
49.286567687988281000
52.753040313720703000
49.584823608398437000
无中间暂存SumFloat
49.286575317382813000
52.753044325858355000
49.584826521575451000
float类型的精度是24位(二进制),十进制自第8位(小数点后第6位,10-6)出现偏差。
double类型
函数
_PC_24
_PC_53
_PC_64
普通求和Sum
47.176055908203125000
52.227423932615110000
50.595782341990436000
误差修正KanhanSum
47.176059722900391000
52.227423932615132000
50.595782341990436000
无中间暂存SumFloat
47.176055908203125000
52.227423932615110000
50.595782341990414000
double类型的精度是53位(二进制),十进制自第16位(小数点后第14位,10-14)出现偏差。
奇怪的是,优化的KanhanSum()不仅没能提高精度反而降低了精度。
本文地址:http://com.8s8s.com/it/it24143.htm