如果您计算((int64_t)1 << 57) * 100
or * 256
,您将有一个有符号整数溢出,这将导致您的代码具有未定义的行为。相反,如果您使用uint64_t
and 值,那么您的代码将是明确定义的,但行为不端。
然而,对于几乎高达(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;
}