((int64_t)1 << 57) * 100
またはを計算* 256
すると、符号付き整数のオーバーフローが発生し、コードが未定義の動作をすることになります。代わりにuint64_t
と の値を使用した場合、コードは明確に定義されていますが、明らかに動作が悪いことになります。
ただし、ほぼ までの数に対してこれを機能させることは可能(1 << 63 / 1.2)
です。
元の数値を 32 だけ右にシフトされた最上位 32 ビットと最下位 32 ビットに分割できる場合y
、これに を掛けます。uint64_t
(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;
}