如何在int64_t = int64_t * float上保持精度?

时间:2016-04-26 09:00:18

标签: c integer-division single-precision

我希望int64_t[0.01..1.2]范围内的因子进行修正,精度约为0.01。天真的实施将是:

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

不幸的是,如果我将factor转换为int32或将y转换为float,我将会失去精确度。

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

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

如果我的输入值可能大于1<<56

,如何解决此问题

情节扭曲:

我在32位架构上运行,其中int64_t是模拟类型,我不支持双精度。该架构是ADI公司的SHARC。

3 个答案:

答案 0 :(得分: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位更接近。

答案 1 :(得分:3)

如果计算((int64_t)1 << 57) * 100* 256,则会产生有符号整数溢出,这会导致代码具有未定义的行为。如果您使用了uint64_t和值,那么您的代码将被明确定义但定义不正常。

但是,这可以使数字工作几乎达到(1 << 63 / 1.2)

如果yuint64_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;
}

答案 2 :(得分: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不会溢出。