定点运算中的单精度
Single precision in Fixed Point Arithmetic
对于使用定点算法的泰勒级数计算,我需要最多 6 位小数的精度。我尝试了不同的定点格式来实现小数点后 6 位的精度。
例如,
使用 s16.15(左移 15)格式,我得到了最多 2 个小数位精度。1 个符号位,16 个整数位和 15 个小数位。
对于 s8.23(左移 23 位)格式最多 4 位小数和 s4.27(左移 27 位)格式,精度仍然相同。我期待情况会有所改善。
下面是泰勒级数展开,计算某点a附近的自然对数。
所以q=x-a,x是1和2之间的用户输入。
// These are converted constants into s4.27 fixed point format
const int32_t con=0x0B8AA3B3; //1.44269504088895
const int32_t c0=0x033E647E; //0.40546510810816
const int32_t c1=0x05555555; //0.66666666666666
const int32_t c2=0x01C71C72; //0.222222222222
const int32_t c3=0x00CA4588; //0.0987654321
const int32_t c4=0x006522C4; //0.04938271605
const int32_t c5=0x0035F069; //0.02633744856
const int32_t c6=0x001DF757; //0.01463191587
//Expanded taylor series
taylor=c0+mul(q,(c1-mul(q,(c2+mul(q,(c3-mul(q,(c4-mul(q,(c5+mul(q,c6)))))))))));
// Multiplication function
int32_t mul(int32_t x, int32_t y)
{
int32_t mul;
mul=((((x)>>13)*((y)>>13))>>1); // for s4.27 format, the best possible right shift
return mul;
}
上面提到的代码片段在 C 中使用。
我需要的结果:0.584963 但我得到的结果是:0.584949
我怎样才能达到更高的精度?
OP 的 mul()
丢掉了太多的精度。
(x)>>13)*((y)>>13)
立即丢弃 x
和 y
.
的最低有效 13 位
而是执行 64 位乘法
int32_t mul_better(int32_t x, int32_t y) {
int64_t mul = x;
mul *= y;
mul >>= 27;
// Code may want to detect overflow here (not shown)
return (int32_t) mul;
}
更好的是,在丢弃最低有效位之前将乘积四舍五入到最接近的值(接近偶数)。简化是可能的。下面的详细代码是说明性的。
int32_t mul_better(int32_t x, int32_t y) {
int64_t mul = x;
mul *= y;
int32_t least = mul % ((int32_t)1 << 27);
mul /= (int32_t)1 << 27;
int carry = 0;
if (least >= 0) {
if (least > ((int32_t)1 << 26) carry = 1;
else if ((least == ((int32_t)1 << 26)) && (mul % 2)) carry = 1;
} else {
if (-least > ((int32_t)1 << 26) carry = -1;
else if ((-least == ((int32_t)1 << 26)) && (mul % 2)) carry = -1;
}
return (int32_t) (mul + carry);
}
int32_t mul(int32_t x, int32_t y) {
int64_t mul = x;
mul *= y;
return mul >> 27;
}
void foo(double x) {
int32_t q = (int32_t) (x * (1 << 27)); // **
int32_t taylor =
c0 + mul(q, (c1 - mul(q, (c2 + mul(q,
(c3 - mul(q, (c4 - mul(q, (c5 + mul(q, c6)))))))))));
printf("%f %f\n", x, taylor * 1.0 / (1 << 27));
}
int main(void) {
foo(0.303609);
}
输出
0.303609 0.584963
** 也可以在这里舍入而不是简单地将 FP 截断为整数。
对于使用定点算法的泰勒级数计算,我需要最多 6 位小数的精度。我尝试了不同的定点格式来实现小数点后 6 位的精度。
例如, 使用 s16.15(左移 15)格式,我得到了最多 2 个小数位精度。1 个符号位,16 个整数位和 15 个小数位。
对于 s8.23(左移 23 位)格式最多 4 位小数和 s4.27(左移 27 位)格式,精度仍然相同。我期待情况会有所改善。
下面是泰勒级数展开,计算某点a附近的自然对数。
所以q=x-a,x是1和2之间的用户输入。
// These are converted constants into s4.27 fixed point format
const int32_t con=0x0B8AA3B3; //1.44269504088895
const int32_t c0=0x033E647E; //0.40546510810816
const int32_t c1=0x05555555; //0.66666666666666
const int32_t c2=0x01C71C72; //0.222222222222
const int32_t c3=0x00CA4588; //0.0987654321
const int32_t c4=0x006522C4; //0.04938271605
const int32_t c5=0x0035F069; //0.02633744856
const int32_t c6=0x001DF757; //0.01463191587
//Expanded taylor series
taylor=c0+mul(q,(c1-mul(q,(c2+mul(q,(c3-mul(q,(c4-mul(q,(c5+mul(q,c6)))))))))));
// Multiplication function
int32_t mul(int32_t x, int32_t y)
{
int32_t mul;
mul=((((x)>>13)*((y)>>13))>>1); // for s4.27 format, the best possible right shift
return mul;
}
上面提到的代码片段在 C 中使用。
我需要的结果:0.584963 但我得到的结果是:0.584949
我怎样才能达到更高的精度?
OP 的 mul()
丢掉了太多的精度。
(x)>>13)*((y)>>13)
立即丢弃 x
和 y
.
而是执行 64 位乘法
int32_t mul_better(int32_t x, int32_t y) {
int64_t mul = x;
mul *= y;
mul >>= 27;
// Code may want to detect overflow here (not shown)
return (int32_t) mul;
}
更好的是,在丢弃最低有效位之前将乘积四舍五入到最接近的值(接近偶数)。简化是可能的。下面的详细代码是说明性的。
int32_t mul_better(int32_t x, int32_t y) {
int64_t mul = x;
mul *= y;
int32_t least = mul % ((int32_t)1 << 27);
mul /= (int32_t)1 << 27;
int carry = 0;
if (least >= 0) {
if (least > ((int32_t)1 << 26) carry = 1;
else if ((least == ((int32_t)1 << 26)) && (mul % 2)) carry = 1;
} else {
if (-least > ((int32_t)1 << 26) carry = -1;
else if ((-least == ((int32_t)1 << 26)) && (mul % 2)) carry = -1;
}
return (int32_t) (mul + carry);
}
int32_t mul(int32_t x, int32_t y) {
int64_t mul = x;
mul *= y;
return mul >> 27;
}
void foo(double x) {
int32_t q = (int32_t) (x * (1 << 27)); // **
int32_t taylor =
c0 + mul(q, (c1 - mul(q, (c2 + mul(q,
(c3 - mul(q, (c4 - mul(q, (c5 + mul(q, c6)))))))))));
printf("%f %f\n", x, taylor * 1.0 / (1 << 27));
}
int main(void) {
foo(0.303609);
}
输出
0.303609 0.584963
** 也可以在这里舍入而不是简单地将 FP 截断为整数。