我正在编写一个基于无符号整数的图像类。我当前使用 uint8_t 和 uint16_t 缓冲区来处理 8 位和 16 位 RGBA 像素,要从 16 位转换为 8 位,我只需获取 16 位值,除以 std::numeric_limits< uint16_t >:: max() 转换为 double,然后乘以 255。
但是,如果我想要一个每个 RGBA 分量都有 64 位无符号整数的图像(我知道,它高得离谱),我该如何找到 0 和 1 之间的浮点数/双精度数来表示 0 和 1 之间的距离我的像素值的最大 uint64_t 是?我认为转换为双精度数不起作用,因为双精度数通常是 64 位浮点数,并且您无法捕获 64 位浮点数中的所有 64 位无符号整数值。不转换为浮点数/双精度数的除法只会给我 0 或有时 1。
查找 0 到 1 之间的浮点值(代表 0 与无符号 64 位值的最大可能值之间的距离)的最准确方法是什么?
查找 0 到 1 之间的浮点值(代表 0 与无符号 64 位值的最大可能值之间的距离)的最准确方法是什么?
可以直接将 [0...264) 范围内的整数值映射到 [0 ... 1.0)。
从
uint64_t
转换为 double
。
缩放 264 @Mark Ransom
#define TWO63 0x8000000000000000u
#define TWO64f (TWO63*2.0)
double map(uint64_t u) {
double y = (double) u;
return y/Two64f;
}
遗嘱地图
[263...264) 至 [0.5 ... 1.0) 范围内的整数值:252 不同的
double
值。double
值。double
值。double
值。double
值。
将 [0...264) 范围内的整数值映射到 [0 ... 1.0] 更困难。 (注意
]
与 )
。
[2021 年 2 月] 我认为这个答案需要对上限情况进行重新解释。 返回的潜在值包括 1.0。
双映射(uint64_t u){ 双 y = (双) u; y /= Two64f; 如果(y >= 1.0){ y = nextafter(y, 0.0); } }
[2024 年 12 月]
替代方案:
向下舍入
一种稍微不太准确的方法,但更一致:
double map_round_down(uint64_t u) {
int save_round = fegetround();
fesetround(FE_TOWARDZERO /* or FE_DOWNWARD */); // Maybe add error checking?
double y = (double) u;
y /= Two64f;
fesetround(save_round);
return y;
}
您可以从以下 Java 的 java.util.Random
nextDouble()
方法的代码开始。它需要 53 位并由它们形成一个双精度数:
return (((long)next(26) << 27) + next(27))
/ (double)(1L << 53);
我将使用 long 的最高有效 26 位作为移位值,并使用接下来的 27 位来填充低位。这会丢弃输入的最低有效 64-53 = 11 位。
如果区分非常小的值特别重要,您还可以使用次正规数,
nextDouble()
不会返回。
OP 要求 C++,所以这里是: (假设编译器知道类型
__int64
,这可能是 Visual Studio 主义。)
double asDouble(unsigned __int64 v)
{
return ((__int64)(v >> 11)) / (double)(1L << 53);
}
或者,如果你不介意时髦的演员阵容:
double asDouble(unsigned __int64 v)
{
// the 0x3FF sets the exponent to the 0..1 range.
unsigned __int64 vv == (v >> 11) | (0x3FFL << 53);
return *(double*)&vv;
}