我需要根据非常数因子将值从一个单位转换为另一个单位。输入值范围为0到1073676289,范围值范围为0到1155625.转换可以这样描述:
output = input * (range / 1073676289)
我自己的初始定点实现感觉有点笨拙:
// Input values (examples)
unsigned int input = 536838144; // min 0, max 1073676289
unsigned int range = 1155625; // min 0, max 1155625
// Conversion
unsigned int tmp = (input >> 16) * ((range) >> 3u);
unsigned int output = (tmp / ((1073676289) >> 16u)) << 3u;
我的代码可以改进更简单或更准确吗?
答案 0 :(得分:6)
这将为您提供没有浮点值的最佳精度,结果将四舍五入为最接近的整数值:
output = (input * (long long) range + 536838144) / 1073676289;
答案 1 :(得分:5)
问题是input * range
会溢出32位整数。通过使用64位整数修复该问题。
uint64_least_t tmp;
tmp = input;
tmp = tmp * range;
tmp = tmp / 1073676289ul;
output = temp;
答案 2 :(得分:3)
快速浏览谷歌会引起我的注意<{3}}
它是标题中的c库,用于管理32位或64位整数的定点数学运算。
使用以下代码进行一些实验:
#include <stdio.h>
#include <stdint.h>
#define FIXEDPT_BITS 64
#include "fixedptc.h"
int main(int argc, char ** argv)
{
unsigned int input = 536838144; // min 0, max 1073676289
unsigned int range = 1155625; // min 0, max 1155625
// Conversion
unsigned int tmp = (input >> 16) * ((range) >> 3u);
unsigned int output = (tmp / ((1073676289) >> 16u)) << 3u;
double output2 = (double)input * ((double)range / 1073676289.0);
uint32_t output3 = fixedpt_toint(fixedpt_xmul(fixedpt_fromint(input), fixedpt_xdiv(fixedpt_fromint(range), fixedpt_fromint(1073676289))));
printf("baseline = %g, better = %d, library = %d\n", output2, output, output3);
return 0;
}
得到以下结果:
baseline = 577812, better = 577776, library = 577812
显示比您的代码更好的精度(匹配浮点)。在引擎盖下,它没有做任何非常复杂的事情(并且根本不能以32位工作)
/* Multiplies two fixedpt numbers, returns the result. */
static inline fixedpt
fixedpt_mul(fixedpt A, fixedpt B)
{
return (((fixedptd)A * (fixedptd)B) >> FIXEDPT_FBITS);
}
/* Divides two fixedpt numbers, returns the result. */
static inline fixedpt
fixedpt_div(fixedpt A, fixedpt B)
{
return (((fixedptd)A << FIXEDPT_FBITS) / (fixedptd)B);
}
但它确实表明你可以获得你想要的精度。你只需要64位就可以了
答案 3 :(得分:0)
你不会比output = input * (range / 1073676289)
如下面的评论中所述,如果您已完成整数操作,那么对于range < 1073676289
:range / 1073676289 == 0
,那么您可以选择:
output = range < 1073676289 ? 0 : input
如果那不是您想要的,那么您实际上需要精确度
output = (input * range) / 1073676289
将是最佳选择。
如果你需要做很多这些,那么我建议你使用double
并让你的编译器矢量化你的操作。精度也可以。