2

我需要根据一个非常数因子将一个值从一个单位转换为另一个单位。输入值范围为 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;

我的代码可以改进得更简单或更准确吗?

4

4 回答 4

6

这将为您提供没有浮点值的最佳精度,结果将四舍五入为最接近的整数值:

output = (input * (long long) range + 536838144) / 1073676289;
于 2012-12-04T15:15:38.553 回答
5

问题是这input * range会溢出一个 32 位整数。通过使用 64 位整数来解决这个问题。

uint64_least_t tmp;
tmp = input;
tmp = tmp * range;
tmp = tmp / 1073676289ul;
output = temp;
于 2012-12-04T15:12:50.393 回答
3

一次快速的谷歌之旅让我注意到了http://sourceforge.net/projects/fixedptc/

它是头文件中的 ac 库,用于管理 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 位就可以做到

于 2012-12-04T15:52:00.003 回答
0

你不会再简单了output = input * (range / 1073676289)

如以下评论中所述,如果您被限制为整数运算,那么 for range < 1073676289:range / 1073676289 == 0所以您最好使用:

output = range < 1073676289 ? 0 : input

如果那不是您想要的并且您实际上想要精确,那么

output = (input * range) / 1073676289

将是要走的路。

如果你需要做很多这些,那么我建议你使用double并让你的编译器矢量化你的操作。精度也可以。

于 2012-12-04T15:00:06.557 回答