舍入到 IEEE 754 精度但保留二进制格式
Round to IEEE 754 precision but keep binary format
如果我将十进制数 3120.0005 转换为浮点数(32 位)表示形式,该数字将向下舍入为 3120.00048828125。
假设我们使用比例为 10^12 的定点数,则 1000000000000 = 1.0 和 3120000500000000 = 3120.0005。
formula/algorithm 是多少才能向下舍入到最接近的 IEEE 754 精度以获得 3120000488281250?
我还需要一种方法来获得四舍五入的结果 (3120000732421875)。
如果除以小数比例因子,您会找到最接近的可表示浮点数。对于另一个方向的舍入,可以使用 std::nextafter
:
#include <float.h>
#include <math.h>
#include <stdio.h>
long long scale_to_fixed(float f)
{
float intf = truncf(f);
long long result = 1000000000000LL;
result *= (long long)intf;
result += round((f - intf) * 1.0e12);
return result;
}
/* not needed, always good enough to use (float)(n / 1.0e12) */
float scale_from_fixed(long long n)
{
float result = (n % 1000000000000LL) / 1.0e12;
result += n / 1000000000000LL;
return result;
}
int main()
{
long long x = 3120000500000000;
float x_reduced = scale_from_fixed(x);
long long y1 = scale_to_fixed(x_reduced);
long long yfloor = y1, yceil = y1;
if (y1 < x) {
yceil = scale_to_fixed(nextafterf(x_reduced, FLT_MAX));
}
else if (y1 > x) {
yfloor = scale_to_fixed(nextafterf(x_reduced, -FLT_MAX));
}
printf("%lld\n%lld\n%lld\n", yfloor, x, yceil);
}
结果:
3120000488281250
3120000500000000
3120000732421875
为了处理 float
由 1e12
缩放的值,并计算下一个更大的 2 的幂,例如"rounding up (3120000732421875)"
,关键是理解您正在从 x / 1.0e12
的 32 位表示中寻找下一个更大的 2 的幂。虽然您可以从数学上得出该值,但 float
和 unsigned
(或 uint32_t
)之间的 union
提供了一种直接的方法来解释存储的 32 位浮点值- 作为无符号值的点数。1
一个简单的例子,利用联合 prev
来保存 x
的减少值和一个单独的实例 next
保存无符号值 (+1
) 可以是:
#include <stdio.h>
#include <inttypes.h>
int main (void) {
uint64_t x = 3120000500000000;
union { /* union between float and uint32_t */
float f;
uint32_t u;
} prev = { .f = x / 1.0e12 }, /* x reduced to float, pwr of 2 as .u */
next = { .u = prev.u + 1u }; /* 2nd union, increment pwr of 2 by 1 */
printf ("prev : %" PRIu64 "\n x : %" PRIu64 "\nnext : %" PRIu64 "\n",
(uint64_t)(prev.f * 1e12), x, (uint64_t)(next.f * 1e12));
}
示例Use/Output
$ ./bin/pwr2_prev_next
prev : 3120000488281250
x : 3120000500000000
next : 3120000732421875
脚注:
1. 作为替代方案,您可以使用指向 char
的 指针来保存浮点数的地址键入并将存储在该位置的 4 字节值解释为 unsigned
,而 运行 与 C11 Standard - §6.5 Expressions (p6,7)("Strict Aliasing Rule")不冲突,但首选 union
。
如果我将十进制数 3120.0005 转换为浮点数(32 位)表示形式,该数字将向下舍入为 3120.00048828125。
假设我们使用比例为 10^12 的定点数,则 1000000000000 = 1.0 和 3120000500000000 = 3120.0005。
formula/algorithm 是多少才能向下舍入到最接近的 IEEE 754 精度以获得 3120000488281250? 我还需要一种方法来获得四舍五入的结果 (3120000732421875)。
如果除以小数比例因子,您会找到最接近的可表示浮点数。对于另一个方向的舍入,可以使用 std::nextafter
:
#include <float.h>
#include <math.h>
#include <stdio.h>
long long scale_to_fixed(float f)
{
float intf = truncf(f);
long long result = 1000000000000LL;
result *= (long long)intf;
result += round((f - intf) * 1.0e12);
return result;
}
/* not needed, always good enough to use (float)(n / 1.0e12) */
float scale_from_fixed(long long n)
{
float result = (n % 1000000000000LL) / 1.0e12;
result += n / 1000000000000LL;
return result;
}
int main()
{
long long x = 3120000500000000;
float x_reduced = scale_from_fixed(x);
long long y1 = scale_to_fixed(x_reduced);
long long yfloor = y1, yceil = y1;
if (y1 < x) {
yceil = scale_to_fixed(nextafterf(x_reduced, FLT_MAX));
}
else if (y1 > x) {
yfloor = scale_to_fixed(nextafterf(x_reduced, -FLT_MAX));
}
printf("%lld\n%lld\n%lld\n", yfloor, x, yceil);
}
结果:
3120000488281250
3120000500000000
3120000732421875
为了处理 float
由 1e12
缩放的值,并计算下一个更大的 2 的幂,例如"rounding up (3120000732421875)"
,关键是理解您正在从 x / 1.0e12
的 32 位表示中寻找下一个更大的 2 的幂。虽然您可以从数学上得出该值,但 float
和 unsigned
(或 uint32_t
)之间的 union
提供了一种直接的方法来解释存储的 32 位浮点值- 作为无符号值的点数。1
一个简单的例子,利用联合 prev
来保存 x
的减少值和一个单独的实例 next
保存无符号值 (+1
) 可以是:
#include <stdio.h>
#include <inttypes.h>
int main (void) {
uint64_t x = 3120000500000000;
union { /* union between float and uint32_t */
float f;
uint32_t u;
} prev = { .f = x / 1.0e12 }, /* x reduced to float, pwr of 2 as .u */
next = { .u = prev.u + 1u }; /* 2nd union, increment pwr of 2 by 1 */
printf ("prev : %" PRIu64 "\n x : %" PRIu64 "\nnext : %" PRIu64 "\n",
(uint64_t)(prev.f * 1e12), x, (uint64_t)(next.f * 1e12));
}
示例Use/Output
$ ./bin/pwr2_prev_next
prev : 3120000488281250
x : 3120000500000000
next : 3120000732421875
脚注:
1. 作为替代方案,您可以使用指向 char
的 指针来保存浮点数的地址键入并将存储在该位置的 4 字节值解释为 unsigned
,而 运行 与 C11 Standard - §6.5 Expressions (p6,7)("Strict Aliasing Rule")不冲突,但首选 union
。