它是一个嵌入式平台,为什么会出现这种限制。
original equation: 0.02035*c*c - 2.4038*c
这样做了:
int32_t val = 112; // this value is arbitrary
int32_t result = (val*((val * 0x535A8) - 0x2675F70));
result = result>>24;
精度仍然很差。当我们乘以val*0x535A8
有没有办法我们可以通过四舍五入来进一步提高精度,但不使用任何浮点数,双精度或除法。
答案 0 :(得分:2)
如何将你的常数缩放10000.你得到的最大数字是2035 * 120 * 120 - 24038 * 120 = 26419440,远低于2 ^ 31的限制。所以也许没有必要在这里进行真正的比特调整。
如Joe Hass所述,您的问题是您将精确位移到垃圾箱中。
将小数点数向左移动2或向左移动10实际上并不重要。只是假装你的小数点不在最后一位后面,而是在移位位置。如果继续使用结果进行计算,则移位2可能更容易处理。如果您只想输出结果,请按上述建议移动十次幂,转换数字并从右侧插入小数点5个字符。
答案 1 :(得分:2)
问题不在于精确度。你正在使用大量的东西。
我怀疑问题在于您正在比较转换为int
的两种不同方法。第一个是double
的演员,第二个是右移的截断。
将浮点数转换为整数只会丢弃小数部分,导致舍入为零;右移会使向下舍入或下限。对于正数,没有区别,但对于负数,这两种方法相互之间会相差1。请参阅http://ideone.com/rkckuy上的示例和Wikipedia处的一些背景信息。
您的原始代码很容易修复:
int32_t result = (val*((val * 0x535A8) - 0x2675F70));
if (result < 0)
result += 0xffffff;
result = result>>24;
的结果
您可能还会确定正确的班次结果是否正常。转换错误不大于其他方法,只是不同。
修改:如果您想要进行舍入而不是截断,答案就更容易了。
int32_t result = (val*((val * 0x535A8) - 0x2675F70));
result = (result + (1L << 23)) >> 24;
我将与其他一些人一起建议您使用常量表达式来替换这些魔术常量,并记录它们的派生方式。
static const int32_t a = (int32_t)(0.02035 * (1L << 24) + 0.5);
static const int32_t b = (int32_t)(2.4038 * (1L << 24) + 0.5);
int32_t result = (val*((val * a) - b));
答案 2 :(得分:1)
吉文斯:
假设1&lt; = c&lt; = 120,
原始等式:0.02035 * c * c - 2.4038 * c
然后-70.98586&lt; f(c)&lt; 4.585
- &GT; -71 <= result <= 5
将f(c)四舍五入到最接近的int32_t
参数A = 0.02035,B = 2.4038
A&amp; B可能会随后编译而改变,但不会在运行时更改。
允许编码人员输入0.02035&amp; 2.4038。这里显示的关键组件和其他人用它来缩放0.02035到2的幂等因子,做等式(简化为形式(A * c - B)* c)并将结果缩放。< / p>
重要功能:
1确定A和B时,确保编译时浮点乘法和最终转换通过循环而不是截断发生。使用正值,+ 0.5
实现了这一点。如果没有舍入的答案UD_A*UD_Scaling
可能会在一个整数下结束,并在转换为int32_t
时截断0.999999
2我们不是在运行时进行昂贵的划分,而是&gt;&gt; (右移)。通过添加一半除数(由@Joe Hass建议),在除法之前,我们得到一个非常圆润的答案。在此/
进行编码非常重要 ,因为some_signed_int / 4
和some_signed_int >> 2
不会以相同的方式进行。使用2的补码时,>>
会截断INT_MIN
,而/
会截断为0。
#define UD_A (0.02035)
#define UD_B (2.4038)
#define UD_Shift (24)
#define UD_Scaling ((int32_t) 1 << UD_Shift)
#define UD_ScA ((int32_t) (UD_A*UD_Scaling + 0.5))
#define UD_ScB ((int32_t) (UD_B*UD_Scaling + 0.5))
for (int32_t val = 1; val <= 120; val++) {
int32_t result = ((UD_A*val - UD_B)*val + UD_Scaling/2) >> UD_Shift;
printf("%" PRId32 "%" PRId32 "\n", val, result);
}
示例差异:
val, OP equation, OP code, This code
1, -2.38345, -3, -2
54, -70.46460, -71, -70
120, 4.58400, 4, 5
这是一个新答案。我的旧+1回答已删除。
答案 3 :(得分:0)
如果您的输入使用最多7位并且您有32位可用,那么您最好的选择是将所有位移到尽可能多的位并使用它:
int32_t result;
result = (val * (int32_t)(0.02035 * 0x1000000)) - (int32_t)(2.4038 * 0x1000000);
result >>= 8; // make room for another 7 bit multiplication
result *= val;
result >>= 16;
在编译时,优化编译器将进行常量转换。