如何将浮点数保存为 2 个字节?

Posted

技术标签:

【中文标题】如何将浮点数保存为 2 个字节?【英文标题】:How do I save a floating-point number in 2 bytes? 【发布时间】:2012-05-02 13:35:54 【问题描述】:

是的,我知道 IEEE-754 半精度标准,是的,我知道在该领域所做的工作。简而言之,我试图将一个简单的浮点数(如 52.11.25)保存在 2 个字节中。

我在Java 和C# 中尝试了一些实现,但它们通过解码不同的数字而破坏了输入值。你输入32.1,编码解码后你得到32.0985

有什么方法可以在不破坏输入值的情况下以 16 位存储浮点数?

非常感谢。

【问题讨论】:

二进制浮点不能编码32.1 你需要编码什么范围的数字,它们有多少个有效数字?考虑十进制定点或浮点数。 您可以将其存储为unsigned short,其中一些位用于指数部分吗?然后,您将手动从这种格式转换回常规单精度 float CodeInChaos 所说的——逐字逐句... 您的问题陈述需要更加具体。两个字节最多可以表示 65536 个不同的浮点值。哪些是你想要的?最坏的情况是,你可以有一个“我关心的 65536 个浮点值”的表,然后查找这个两字节的值。 【参考方案1】:

您可以将三位数字存储在 BCD 中,并将剩余的四位用于小数点位置:

52.1 = 521 * 10 ^ -1 => 0x1521
1.25 = 125 * 10 ^ -2 => 0x2125

这将为您提供从 0.0000000000000001 到 999 的范围。当然,您可以为小数点添加偏移量,例如范围为 0.0000000001 到 999000000。


四位用于小数点放置的简单实现,其余用于值。没有任何错误检查,也没有彻底检查。 (使用!= 比较双精度时,某些值可能存在精度问题。)

public static short Encode(double value) 
  int cnt = 0;
  while (value != Math.Floor(value)) 
    value *= 10.0;
    cnt++;
  
  return (short)((cnt << 12) + (int)value);


public static double Decode(short value) 
  int cnt = value >> 12;
  double result = value & 0xfff;
  while (cnt > 0) 
    result /= 10.0;
    cnt--;
  
  return result;

例子:

Console.WriteLine(Encode(52.1));
Console.WriteLine(Decode(4617));

输出:

4617
52.1

【讨论】:

@Geotarget:您可以将 4 位数字压缩成两个字节,但是您只剩下两位用于描述小数点的位置。对于数字较少的数字,您只需用零填充,即1.5001.51.500 相同。 你能展示浮点到二进制编码/解码函数的例子吗?很抱歉,但我真的不明白发生了什么。 @Geotarget:我在上面添加了一个简单的实现。【参考方案2】:

C# 对此没有内置功能,但您可以尝试使用定点方法。

8,8 定点示例(逗号前 8,逗号后 8):

float value = 123.45;
ushort fixedIntValue = (ushort)(value * 256);

这样,号码的存储方式如下: XXXXXXXXX,XXXXXXXXX

您可以使用以下方法再次检索浮动:

float value = fixedIntValue / 256f;

【讨论】:

这也有一个有限的精度。 52.1 变为 52.09765625。 好吧,你不可能拥有一切。如果您想要更多,您可以尝试 6,10 定点或使用 4 字节。 操作并不要求一切,只是为了得到完全相同的值。如果一个有限的范围是可以接受的,这完全不是不合理的。您只需要使用与二进制浮点/定点数不同的方法。【参考方案3】:

您确定需要这样的微优化,而不是简单地使用floatdouble

存储short 并理解这一点是否会更好地为您服务,例如,将其除以 100 得到实际数字? (例如,您的 52.1 和 1.25 示例可以存储为 5210 和 125)我认为这可能是您的最佳解决方案。

如果您打算使用实际的浮点数,您可以获取解码后的数字并将其四舍五入到 x 个有效数字(在您的示例中为 3),这通常可以让您返回与开始时相同的数字(请注意,是的,这是故意模糊的 - 除非您存储原件,否则您不能保证获得原件)。

【讨论】:

我可以看到这被用于网络通信,例如游戏。它们不需要非常精确地发送例如位置数据,但网络流量受到严重限制,当您必须每秒多次服务数百名玩家时,2 字节和 4 字节之间的差异非常明显。【参考方案4】:

问题是您不能在 any 二进制浮点类型中精确表示 32.1

在单精度中,最接近的可表示值是 32.099998。 在半精度下,它显然是 32.0985。

您可以考虑使用十进制浮点类型,但这种解决方案并不是半精度独有的。

【讨论】:

半精度值使用 11 位作为有效位(前导位 1 是隐含的)。在区间 [32,64) 中,其中 6 位用于整数部分,剩下 5 位用于小数部分。所以在那个域 [32,64) 中,可表示的值正好是 1/(2**5) = 1/32 的倍数。最接近32.1 的将是 32+3/32(又名 1027/32),即32.09375。所以你的“显然”是不正确的,毕竟。我不知道提问者的例子来自哪里。对于半精度值,通常只输出 3 位十进制数字,因此 "32.1" 将是通常的精度。【参考方案5】:

有 4,278,190,080 个 32 位浮点值,不包括 NaN 和无穷大。两个字节中的 16 位有 65,536 个值。显然,不可能将所有浮点值唯一地编码在两个字节中。

你想编码哪些?

即使对于符号和指数的单个值(例如,从 4 到 8 的所有浮点值,不包括 8),也有 8,388,608 个浮点值,因此您甚至无法将它们编码为两个字节。

您必须将自己限制为要编码的值的一小部分。完成此操作后,人们可能会对如何对其进行编码提出建议。您要解决的实际问题是什么?

【讨论】:

【参考方案6】:

从您的示例中,您想要存储 3 位数字和一个小数点。您可以简单地将 11 个符号的“字母”编码为 4 位代码,并将 4 x 4 位存储在 2 个字节中。

【讨论】:

以上是关于如何将浮点数保存为 2 个字节?的主要内容,如果未能解决你的问题,请参考以下文章

将浮点数转换为PHP中的字节数组

Abplc浮点数怎么传给4个字节

C++ 将浮点数保存并加载到二进制文件中,由指针寻址

当 .toFixed(2) 小数点后为零时,如何将浮点数格式化为整数?

将浮点数舍入到 N 个小数位

Javascript将浮点数转换为指数[重复]