如何将浮点数保存为 2 个字节?
Posted
技术标签:
【中文标题】如何将浮点数保存为 2 个字节?【英文标题】:How do I save a floating-point number in 2 bytes? 【发布时间】:2012-05-02 13:35:54 【问题描述】:是的,我知道 IEEE-754 半精度标准,是的,我知道在该领域所做的工作。简而言之,我试图将一个简单的浮点数(如 52.1
或 1.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.5
与001.5
或1.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】:您确定需要这样的微优化,而不是简单地使用float
或double
?
存储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 个字节?的主要内容,如果未能解决你的问题,请参考以下文章