我希望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。
答案 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)
。
如果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;
}
答案 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
不会溢出。