【发布时间】:2021-05-09 00:55:13
【问题描述】:
我正在使用 C# 并尝试将四个字节打包成一个浮点数(上下文是游戏开发,其中 RGBA 颜色被打包成一个值)。为此,我使用BitConverter,但某些转换似乎会导致不正确的字节。举个例子(使用字节0, 0, 129, 255):
var before = new [] { (byte)0, (byte)0, (byte)129, (byte)255 };
var f = BitConverter.ToSingle(before, 0); // Results in NaN
var after = BitConverter.GetBytes(f); // Results in bytes 0, 0, 193, 255
使用https://www.h-schmidt.net/FloatConverter/IEEE754.html,我验证了我开头的四个字节(0, 0, 129, 255,相当于二进制00000000000000001000000111111111)代表浮点值4.66338115943e-41。通过翻转字节序(二进制11111111100000010000000000000000),我得到NaN(匹配上面代码中的f)。但是当我将该浮点数转换回字节时,我得到0, 0, 193, 255(注意193,当我期待129时)。
奇怪的是,使用字节 0, 0, 128, 255 运行相同的示例是正确的(浮点值 f 变为 -Infinity,然后转换回字节再次生成 0, 0, 128, 255)。鉴于这一事实,我怀疑 NaN 是相关的。
谁能解释一下这里发生了什么?
更新:问题Converting 2 bytes to Short in C# 被列为重复问题,但这是不准确的。该问题试图将字节转换为一个值(在这种情况下,将两个字节转换为一个短字节)并且不正确的字节序给出了一个意外的值。在我的情况下,实际的浮点值是无关紧要的(因为我没有使用转换后的值作为浮点数)。相反,我试图通过首先转换为浮点数,然后再转换回来,将四个字节有效地重新解释为浮点数直接。如图所示,这种来回有时会返回与我发送的字节不同的字节。
第二次更新:我将简单地回答我的问题。正如 Peter Duniho cmets,BitConverter 永远不会修改您传入的字节,而只是将它们复制到新的内存位置并重新解释结果。但是,正如我的示例所示,可以发送四个字节 (0, 0, 129, 255),这些字节在内部复制并重新解释为浮点数,然后将该浮点数转换回与原始字节不同的字节(0, 0, 193, 255)。
与BitConverter 相关的字节序经常被提及。但是,在这种情况下,我觉得字节顺序不是根本问题。当我调用BitConverter.ToSingle 时,我传入了一个包含四个字节的数组。这些字节代表一些转换为浮点数的二进制(32 位)。通过在函数调用之前更改字节序,我所做的就是更改我发送到函数中的位。无论这些位的 值 是什么,都应该可以将它们转换为浮点数(也是 32 位),然后将浮点数转换回 我发送的相同位。如我的示例所示,使用字节 0, 0, 129, 255(二进制 00000000000000001000000111111111)会产生一个浮点值。我想取那个值(这些位表示的浮点数)并将其转换为原始的四个字节。
在所有情况下都可以在 C# 中实现吗?
【问题讨论】:
-
BitConverter字节顺序可以通过IsLittleEndian属性确定。至于你的具体例子,不清楚为什么如果你想要 RGBA 值你使用float,但你观察到的基本原因是有多个(多于一个)二进制值被处理NaN、Infinity或-Infinity。请记住,字节顺序会影响 byte 顺序,但不会影响位顺序。当您反转 位 时,您没有纠正字节顺序问题,而是破坏了值,因此使用193而不是129。 -
我仍然认为这不是根本问题(列出的重复项与我的问题不相同)。即使考虑到字节序,一些 RGBA 字节是否最终会导致我在上面使用的值,这样转换为浮点数并返回字节会产生不正确的结果?归根结底,我试图将 32 位转换为浮点数并返回到相同的位。这在所有情况下都可以在 C# 中实现吗?
-
@Grimelios
float是System.Single的别名,它是一个值类型。将您的NaN值作为参数传递给BitConverter.GetBytes()通过值而不是引用传递它,这意味着复制的值可能会使用不同的位序列使用 NaN 值初始化。只要指数是FF并且分数不为零,float将是 NaN。所以否,将四个字节转换为float并返回到原始的四个字节在所有情况下都是不可能的。正如彼得已经说过的,这种转换为浮动和返回的动机尚不清楚。 -
@PeterDuniho 感谢您指出
BitConverter并不关心NaN有多种表示形式这一事实。如果您真的重新阅读我所说的话,我从未声称它确实如此。 “传递给BitConverter的4 个字节将直接复制到float值中,然后如果再次传递给BitConverter以转换回数组,将再次逐字复制回来。” 事实上,this isn't really true。他们期望的结果与实际结果不符;确切的语义并不重要。 -
是的,NaN 是一个问题。该标准为 NaN 指定了一系列不同的值,区分安静的 NaN 和 signaling NaNs。坏主意,.NET 不支持它只允许 NaN 的单个值,您会看到它使值正常化。其他情况是 +0 和 -0 之间的区别,.NET 规范化为 +0。从技术上讲,非规范值可以刷新为 0(不应该发生)。取得成功的唯一方法是不这样做,BitConverter.ToInt32() 不会被弄乱。
标签: c# endianness bitconverter