在不使用float,double或division的情况下舍入整数

时间:2014-01-17 17:17:47

标签: c math rounding

它是一个嵌入式平台,为什么会出现这种限制。

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有没有办法我们可以通过四舍五入来进一步提高精度,但不使用任何浮点数,双精度或除法。

4 个答案:

答案 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;

查看http://ideone.com/D0pNPF

的结果

您可能还会确定正确的班次结果是否正常。转换错误不大于其他方法,只是不同。

修改:如果您想要进行舍入而不是截断,答案就更容易了。

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 / 4some_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;

在编译时,优化编译器将进行常量转换。