将浮点数转换为其二进制表示(使用MemoryStream?)

8
我想将给定的 float 转换为其二进制表示。我试图将浮点值写入 MemoryStream,逐字节读取此 MemoryStream 并将字节转换为它们的二进制表示。但是每次尝试都失败了。
  • "无法读取关闭的流"(但我只关闭了写入器)
  • 出于测试目的,我只是写了一个整数(我认为大小为四个字节),当我没有刷新 StreamWriter 时,MemoryStream 的长度为0,而当我这样做时,长度为1。
我确信有更好的方法将 float 转换为二进制,但我也想学习一些关于 MemoryStream 类的知识。
6个回答

16

好的。我总是忘记用哪个类来实现特定的功能 :) 谢谢您的快速回答 :) - Cubi73
1
@Cubinator73:这就是文档的作用。例如,从StreamWriter中可以看到:“实现了一个TextWriter,用于将字符以特定编码写入流中”- 这不是你想要的。 - Jon Skeet
有没有更高效的方法,例如将数据写入 uint(对于 float)和 ulong(对于 double)?使用 byte[] 数组可能需要在堆上构建对象,并且不提供简单的位操作。 - Willem Van Onsem
1
@CommuSoft:对于 double 类型有 BitConverter.DoubleToInt64Bits 函数,但我不知道是否有类似的函数适用于 float 类型。 - Jon Skeet
2
这个页面上找到了一些东西。使用不安全指针进行转换:ulong db=*(ulong*)&d;或者uint fb=*(uint*)&f;。这可能是最有效的方法。 - Willem Van Onsem
@CommuSoft:这基本上就是BitConverter所做的事情,但不需要你使用不安全代码 :) - Jon Skeet

6
使用BitConverter代替MemoryStream:
        // -7 produces "1 10000001 11000000000000000000000"
        static string FloatToBinary(float f)
        {
            StringBuilder sb = new StringBuilder();
            Byte[] ba = BitConverter.GetBytes(f);
            foreach (Byte b in ba)
                for (int i = 0; i < 8; i++)
                {
                    sb.Insert(0,((b>>i) & 1) == 1 ? "1" : "0");
                }
            string s = sb.ToString();
            string r = s.Substring(0, 1) + " " + s.Substring(1, 8) + " " + s.Substring(9); //sign exponent mantissa
            return r;
        }

3

Dotnetfiddle

BitConverter.GetBytes(3.141f)
            .Reverse()
            .Select(x => Convert.ToString(x, 2))
            .Select(x => x.PadLeft(8, '0'))
            .Aggregate("0b", (a, b) => a + "_" + b);

// res = "0b_01000000_01001001_00000110_00100101"

我忍不住使用了一个“小”LINQ查询。它也适用于double类型。


1

当使用StreamWriter时,您可能会遇到陷阱,如下面的代码所示:

        // Write the float
        var f = 1.23456f;
        var ms = new MemoryStream();
        var writer = new StreamWriter(ms);
        writer.Write(f);
        writer.Flush();

        // Read 4 bytes to get the raw bytes (Ouch!)
        ms.Seek(0, SeekOrigin.Begin);
        var buffer = new char[4];
        var reader = new StreamReader(ms);
        reader.Read(buffer, 0, 4);
        for (int i = 0; i < 4; i++)
        {
            Console.Write("{0:X2}", (int)buffer[i]);
        }
        Console.WriteLine();

        // This is what you actually read: human readable text
        for (int i = 0; i < buffer.Length; i++)
        {
            Console.Write(buffer[i]);
        }
        Console.WriteLine();

        // This is what the float really looks like in memory.
        var bytes = BitConverter.GetBytes(f);
        for (int i = 0; i < bytes.Length; i++)
        {
            Console.Write("{0:X2}", (int)bytes[i]);
        }

        Console.ReadLine();

如果你期望在流中只有4个字节,并且读取了这4个字节,一开始看起来一切都很正常。但实际上长度是7,你只读取了浮点数文本表示的前4个字节。

与BitConverter的输出进行比较可以发现,在这里使用StreamWriter并不正确。


最后三个字节是BOM吗? - Cubi73
不,最后3个字节是数字1.23456的456。前4个字节是数字1.23456的1.23(包括小数点)。 - Thomas Weller

0
回答你的第一个问题:在 .Net 中,当你关闭/释放一个读取器/写入器时,底层流也会被关闭/释放。

0

在2017年,.NET添加了以下内容:

对于Float / Single...

int rawBits     = BitConverter.SingleToInt32Bits(myFloat);
float backAgain = BitConverter.Int32BitsToSingle(rawBits);

对于Double...

int rawBits      = BitConverter.SingleToInt32Bits(myDouble);
double backAgain = BitConverter.Int32BitsToSingle(rawBits);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接