4

我想在精度约为int64_t的范围内对一个因子进行校正。天真的实现是:[0.01..1.2]0.01

int64_t apply_correction(int64_t y, float32_t factor)
{
    return y * factor;
}

不幸的是,如果我投射factorint32或如果我投射yfloat.

但是,如果我能确保y它的最大值低于1<<56,我可以使用这个技巧:

(1<<8) * (y / (int32_t)(factor * (1<<8)))

如果我的输入值可以大于 ,我该如何解决这个问题1<<56

剧情转折:

我在 32 位架构上运行,该架构int64_t是模拟类型,并且我不支持双精度。该架构是 Analog Devices 的 SHARC。

4

3 回答 3

3

在整数空间中做呢?

/* factor precision is two decimal places */
int64_t apply_correction(int64_t y, float32_t factor)
{
    return y * (int32_t)(factor * 100) / 100;
}

这确实假设y不是很接近最大值,但它让你比 56 位更接近一点。

于 2016-04-26T09:06:31.160 回答
3

如果您计算((int64_t)1 << 57) * 100or * 256,您将有一个有符号整数溢出,这将导致您的代码具有未定义的行为。相反,如果您使用uint64_tand 值,那么您的代码将是明确定义的,但行为不端。


然而,对于几乎高达(1 << 63 / 1.2).

如果y是,uint64_t您可以将原始数字拆分为最高有效 32 位,右移 32 位,最低有效 32 位,将其乘以(int32_t)(factor * (1 << 8)).

然后,在乘法之后,您不会将最高有效位右移 8 位,而是左移 24 位;然后加在一起:

uint64_t apply_uint64_correction(uint64_t y, float32_t factor)
{
    uint64_t most_significant = (y >> 32) * (uint32_t)(factor * (1 << 8));
    uint64_t least_significant = (y & 0xFFFFFFFFULL) * (uint32_t)(factor * (1 << 8));     
    return (most_significant << 24) + (least_significant >> 8);
}

现在,apply_uint64_correction(1000000000000, 1.2)将导致1199218750000,并且apply_uint64_correction(1000000000000, 1.25)将导致1250000000000


实际上,如果您可以保证 的范围,您可以更精确地使用它factor

uint64_t apply_uint64_correction(uint64_t y, float32_t factor)
{
    uint64_t most_significant = (y >> 32) * (uint32_t)(factor * (1 << 24));
    uint64_t least_significant = (y & 0xFFFFFFFFULL) * (uint32_t)(factor * (1 << 24));     
    return (most_significant << 8) + (least_significant >> 24);
}

apply_uint64_correction(1000000000000, 1.2)1200000047683在我的电脑上给予;float32_t如果有 24 位尾数,这也是您可以获得的最大精度。


上面的算法也适用于有符号的正数,但是由于负数的有符号移位是一个灰色区域,我会记下符号,然后将值转换为uint64_t,可移植地进行计算,然后否定原始符号是消极的。

int64_t apply_correction(int64_t y, float32_t factor) {
    int negative_result = 0;
    uint64_t positive_y = y;
    if (y < 0) {
        negative_result = 1;
        positive_y = -y;
    }

    uint64_t result = apply_uint64_correction(positive_y, factor);
    return negative_result ? -(int64_t)result : result;
}
于 2016-04-26T09:15:10.123 回答
2

只是不要使用浮点数。

int64_t apply_correction(int64_t y, float32_t factor)
{
  int64_t factor_i64 = factor * 100f;

  return (y * factor_i64) / 100ll;
}

这是假设y * factor_i64 * 100不会溢出。

于 2016-04-26T09:10:21.293 回答