int64 到 IEEE double 的按位转换?
Bitwise conversion of int64 to IEEE double?
我试图找到或弄清楚将带符号的 64 位 int(二进制补码,natch)转换为最接近值 IEEE 双精度(64 位)的算法,并保持在按位范围内 operations.What我正在寻找通用的“C-like”伪代码;我在不是 C 的平台上实现了一个玩具 JVM,并且没有本机 int64
类型,所以我在 8 字节数组上运行(幸运的是,它的细节超出了这个范围),这就是数据需要保留的域。
所以:输入是一个 64 位的大端字符串,有符号二进制补码。输出是 IEEE 双精度格式的 64 位大端字符串,表示尽可能接近原始 int64 值。中间是一些掩码、班次等!算法绝对不需要特别聪明或优化。我只是希望能够得到结果并理想地理解过程是什么。
无法追踪到这个,因为我怀疑这是一个不寻常的需求。 在 x86 SSE 中解决了一个平行问题(我认为),但我不会说 SSE,我的尝试和翻译让我感到困惑而不是启发。
希望有人为食谱指出正确的方向,或者理想地解释背后的按位数学,以便我真正理解它。谢谢!
这是一个简单的(在几个方面是错误的)实现,包括一个测试工具。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
double do_convert(int64_t input)
{
uint64_t sign = (input < 0);
uint64_t magnitude;
// breaks on INT64_MIN
if (sign)
magnitude = -input;
else
magnitude = input;
// use your favourite algorithm here instead of the builtin
int leading_zeros = __builtin_clzl(magnitude);
uint64_t exponent = (63 - leading_zeros) + 1023;
uint64_t significand = (magnitude << (leading_zeros + 1)) >> 12;
uint64_t fake_double = sign << 63
| exponent << 52
| significand;
double d;
memcpy(&d, &fake_double, sizeof d);
return d;
}
int main(int argc, char** argv)
{
for (int i = 1; i < argc; i++)
{
long l = strtol(argv[i], NULL, 0);
double d = do_convert(l);
printf("%ld %f\n", l, d);
}
return 0;
}
这里有很多破绽——基本思路是先提取符号位,然后在剩下的过程中将数字视为正数,如果输入是 INT64_MIN,这将不起作用。它也没有正确处理输入 0
,因为在这种情况下它没有正确处理指数。这些扩展留作 reader 的练习。 ;-)
无论如何 - 该算法只是通过计算输入数字的 log2 并偏移 1023 (because floating point) 来计算指数,然后通过将数字向上移动足够远以降低最重要的数字来获得有效数字位,然后向下移回到正确的字段位置。
毕竟,最终替身的组装非常简单。
编辑:
说到 reader 的练习 - 我也使用 _builtin_clzl()
实现了这个程序。您可以根据需要扩展该部分。
我试图找到或弄清楚将带符号的 64 位 int(二进制补码,natch)转换为最接近值 IEEE 双精度(64 位)的算法,并保持在按位范围内 operations.What我正在寻找通用的“C-like”伪代码;我在不是 C 的平台上实现了一个玩具 JVM,并且没有本机 int64
类型,所以我在 8 字节数组上运行(幸运的是,它的细节超出了这个范围),这就是数据需要保留的域。
所以:输入是一个 64 位的大端字符串,有符号二进制补码。输出是 IEEE 双精度格式的 64 位大端字符串,表示尽可能接近原始 int64 值。中间是一些掩码、班次等!算法绝对不需要特别聪明或优化。我只是希望能够得到结果并理想地理解过程是什么。
无法追踪到这个,因为我怀疑这是一个不寻常的需求。
希望有人为食谱指出正确的方向,或者理想地解释背后的按位数学,以便我真正理解它。谢谢!
这是一个简单的(在几个方面是错误的)实现,包括一个测试工具。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
double do_convert(int64_t input)
{
uint64_t sign = (input < 0);
uint64_t magnitude;
// breaks on INT64_MIN
if (sign)
magnitude = -input;
else
magnitude = input;
// use your favourite algorithm here instead of the builtin
int leading_zeros = __builtin_clzl(magnitude);
uint64_t exponent = (63 - leading_zeros) + 1023;
uint64_t significand = (magnitude << (leading_zeros + 1)) >> 12;
uint64_t fake_double = sign << 63
| exponent << 52
| significand;
double d;
memcpy(&d, &fake_double, sizeof d);
return d;
}
int main(int argc, char** argv)
{
for (int i = 1; i < argc; i++)
{
long l = strtol(argv[i], NULL, 0);
double d = do_convert(l);
printf("%ld %f\n", l, d);
}
return 0;
}
这里有很多破绽——基本思路是先提取符号位,然后在剩下的过程中将数字视为正数,如果输入是 INT64_MIN,这将不起作用。它也没有正确处理输入 0
,因为在这种情况下它没有正确处理指数。这些扩展留作 reader 的练习。 ;-)
无论如何 - 该算法只是通过计算输入数字的 log2 并偏移 1023 (because floating point) 来计算指数,然后通过将数字向上移动足够远以降低最重要的数字来获得有效数字位,然后向下移回到正确的字段位置。
毕竟,最终替身的组装非常简单。
编辑:
说到 reader 的练习 - 我也使用 _builtin_clzl()
实现了这个程序。您可以根据需要扩展该部分。