C 不使用浮点、双精度或除法对整数进行舍入
它是一个嵌入式平台,这就是为什么会有这样的限制C 不使用浮点、双精度或除法对整数进行舍入,c,math,rounding,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时,有没有一种方法可以通过四舍五入来进一步提高精度,
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时,有没有一种方法可以通过四舍五入来进一步提高精度,但不使用任何浮点、双精度或除法。将常数按10000进行缩放如何。然后得到的最大数字是2035*120*120-24038*120=26419440,远远低于2^31的限制。所以这里可能没有必要做真正的调整 正如Joe Hass所指出的,你的问题是你把你的精密钻头扔进了垃圾箱
将小数点向左移动2或10实际上并不重要。只要假装小数点不在最后一位的后面,而是在移位的位置。如果您继续计算结果,则按2移位可能更容易处理。如果您只想输出结果,请按上述建议的十次幂进行移位,转换数字并从右侧插入小数点5个字符。如果r input最多使用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;
常量转换将在编译时由优化编译器完成。Givens:
假设1>2不以相同的方式取整。有了2的补码,
向INT\u 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);
}
这是一个新的答案。我原来的+1答案被删除。问题不在于精确性。你用了很多比特 我怀疑问题在于您正在比较两种不同的转换为
int
的方法。第一个是双精度的转换,第二个是右移的截断
将浮点转换为整数只需删除小数部分,就可以得到一个接近零的整数;右移可向下或向下移动。对于正数没有区别,但是对于负数,这两种方法彼此相差1。请参见中的示例和中的一些背景阅读
您的原始代码很容易修复:
int32_t result = (val*((val * 0x535A8) - 0x2675F70));
if (result < 0)
result += 0xffffff;
result = result>>24;
int32_t result=(val*((val*0x535A8)-0x2675F70));
如果(结果<0)
结果+=0xffffff;
结果=结果>>24;
请参阅
您还可以决定右移结果是否正常。转换错误并不比其他方法大,只是不同而已
编辑:如果要进行舍入而不是截断,则答案更简单
int32_t result = (val*((val * 0x535A8) - 0x2675F70));
result = (result + (1L << 23)) >> 24;
int32_t result=(val*((val*0x535A8)-0x2675F70));
结果=(结果+(1L>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));
static const int32_t a=(int32_t)(0.02035*(1L)您的最长整数有多长?输入c
有最大值吗?我想判断您可以使用多少位。我将使用的最大值是120(对于c)。整数可以是32位长。您想要什么精度?您正在进行的计算基本上是将一个相当精确的定点值转换为一个整数,而该整数的最大可能值仅为4。c的最小值是什么?对于仅120个输入值,您可以轻松地使用查找表。我不太明白@Se的答案rgey。通过移位,我们不是会失去精度吗?老实说,我不明白答案。你能再解释一下吗?@UnderDog precision=你使用的位数。float
的精度是23位,double
的精度是52位。通过将整数移位到最大值,我们使用32位的完整精度整数。由于结果是一个整数,我们需要将结果移回正确的数值。除了这里添加的UD\u Scaling/2
之外,我看不出这与问题中的代码有什么不同。如果你看十六进制常数,你会发现它们正是你正在计算的。@Mark Ransom Good point.hmm。操作方法用于计算常数的od未显示。这里是明确的-便于将来参考。我认为,0.02035
等值可能会在符合之间发生变化,但处于大致范围内。不知道OP的方法,OP的缩放常数和此处的缩放常数可能会因A或B的变化而不同。OP粗略地说“精度差”。如果OP总是做与我相同的缩放,除了UD\u缩放/2
,没有更高的精度。答案在整数步中仅从-71到5不等,UD\u缩放/2
得到最好的int32\u t
答案。顺便说一句:在嵌入式环境中,16位int
非常普遍。建议((int32_t)1@chux,谢谢你的建议。我已经在中编辑了它。
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));