【问题标题】:How do I display the binary representation of a float or double?如何显示浮点数或双精度数的二进制表示?
【发布时间】:2008-12-29 13:08:54
【问题描述】:

我想显示浮点数的二进制(或十六进制)表示。我知道如何手动转换(使用方法here),但我有兴趣查看执行相同操作的代码示例。

虽然我对 C++ 和 Java 解决方案特别感兴趣,但我想知道是否有任何语言使它特别容易,所以我将这个 语言不可知。我很想看到其他语言的一些解决方案。

编辑:我已经很好地了解了 C、C++、C# 和 Java。是否有任何替代语言专家想要添加到列表中?

【问题讨论】:

    标签: language-agnostic floating-point


    【解决方案1】:

    C/C++ 很简单。

    union ufloat {
      float f;
      unsigned u;
    };
    
    ufloat u1;
    u1.f = 0.3f;
    

    然后你就输出u1.u。你可以适应this implementation

    翻倍同样简单。

    union udouble {
      double d;
      unsigned long u;
    }
    

    因为双精度是 64 位的。

    Java 更简单一些:使用 Float.floatToRawIntBits() 结合 Integer.toBinaryString()Double.doubleToRawLongBits 结合 Long.toBinaryString()

    【讨论】:

    • 注意!浮点数和双精度数的位大小是标准化的,长整数、整数等的大小......取决于架构!您可以使用特定于实现的类型 __int64 和 __int32(在 Windows 上)。
    • 你可以使用我的codepad.org/g8OtGEqX。那么您可以使用 Integer::type 作为整数类型。我写它是因为 D faq 声称 C++ 不能做到这一点(找到一个至少有 N 位的类型)
    • 代码非常保守地假定类型的大小(因此,如果您将“long long”去掉,它适用于所有实现)。我想人们可以编写一个更好的版本,其中列出了每种基本类型的实际大小并根据这些事实做出决定。大多数 64 位系统都有 64 位长类型。
    • 这是未定义的行为。不允许乱序访问联合。
    • @KerrekSB:这个问题被标记为与语言无关,并且是否定义了访问最后存储的成员以外的联合成员取决于语言。在 C 中,行为是为某些类型定义的,是为其他类型定义的(包括访问具有相同或更小的无符号整数的存储双精度数),并且在某些情况下具有未定义的行为。
    【解决方案2】:

    在 C 中:

    int fl = *(int*)&floatVar;
    

    &floatVar 将获得地址内存,然后(int*) 将是指向此地址内存的指针,最后是 * 以获取 int 中浮动的 4 个字节的值。 然后就可以 printf 二进制格式或者十六进制格式了。

    【讨论】:

      【解决方案3】:

      Java:Google 搜索在 Sun's forums 上找到此链接

      具体来说(我自己没试过)

      long binary = Double.doubleToLongBits(3.14159);
      String strBinary = Long.toBinaryString(binary);
      

      【讨论】:

      • 正是我想要的。在发布问题之前,我在 Google 上搜索了如何在 C++ 中执行此操作。我也应该寻找 Java 解决方案。 :)
      • 我希望strBinary.length() 是 64,但我得到的是 63。有谁知道为什么?
      • 同样,Integer.toBinaryString(Float.floatToIntBits(floatNumber)); 将返回浮点数的字符串位表示。
      【解决方案4】:

      在 .NET(包括 C#)中,BitConverter 接受多种类型,允许访问原始二进制文件;要获得十六进制,ToString("x2") 是最常见的选项(可能包含在实用方法中):

          byte[] raw = BitConverter.GetBytes(123.45);
          StringBuilder sb = new StringBuilder(raw.Length * 2);
          foreach (byte b in raw)
          {
              sb.Append(b.ToString("x2"));
          }
          Console.WriteLine(sb);
      

      奇怪的是,base-64 有一个 1 行转换 (Convert.ToBase64String),但 base-16 需要更多的努力。除非您引用 Microsoft.VisualBasic,在这种情况下:

      long tmp = BitConverter.DoubleToInt64Bits(123.45);
      string hex = Microsoft.VisualBasic.Conversion.Hex(tmp);
      

      【讨论】:

        【解决方案5】:

        我是这样做的:

        /*
        @(#)File:           $RCSfile: dumpdblflt.c,v $
        @(#)Version:        $Revision: 1.1 $
        @(#)Last changed:   $Date: 2007/09/05 22:23:33 $
        @(#)Purpose:        Print C double and float data in bytes etc.
        @(#)Author:         J Leffler
        @(#)Copyright:      (C) JLSS 2007
        @(#)Product:        :PRODUCT:
        */
        
        /*TABSTOP=4*/
        
        #include <stdio.h>
        #include "imageprt.h"
        
        #ifndef lint
        /* Prevent over-aggressive optimizers from eliminating ID string */
        extern const char jlss_id_dumpdblflt_c[];
        const char jlss_id_dumpdblflt_c[] = "@(#)$Id: dumpdblflt.c,v 1.1 2007/09/05 22:23:33 jleffler Exp $";
        #endif /* lint */
        
        union u_double
        {
            double  dbl;
            char    data[sizeof(double)];
        };
        
        union u_float
        {
            float   flt;
            char    data[sizeof(float)];
        };
        
        static void dump_float(union u_float f)
        {
            int exp;
            long mant;
        
            printf("32-bit float: sign: %d, ", (f.data[0] & 0x80) >> 7);
            exp = ((f.data[0] & 0x7F) << 1) | ((f.data[1] & 0x80) >> 7);
            printf("expt: %4d (unbiassed %5d), ", exp, exp - 127);
            mant = ((((f.data[1] & 0x7F) << 8) | (f.data[2] & 0xFF)) << 8) | (f.data[3] & 0xFF);
            printf("mant: %16ld (0x%06lX)\n", mant, mant);
        }
        
        static void dump_double(union u_double d)
        {
            int exp;
            long long mant;
        
            printf("64-bit float: sign: %d, ", (d.data[0] & 0x80) >> 7);
            exp = ((d.data[0] & 0x7F) << 4) | ((d.data[1] & 0xF0) >> 4);
            printf("expt: %4d (unbiassed %5d), ", exp, exp - 1023);
            mant = ((((d.data[1] & 0x0F) << 8) | (d.data[2] & 0xFF)) << 8) |
                      (d.data[3] & 0xFF);
            mant = (mant << 32) | ((((((d.data[4] & 0xFF) << 8) |
                      (d.data[5] & 0xFF)) << 8) | (d.data[6] & 0xFF)) << 8) |
                      (d.data[7] & 0xFF);
            printf("mant: %16lld (0x%013llX)\n", mant, mant);
        }
        
        static void print_value(double v)
        {
            union u_double d;
            union u_float  f;
        
            f.flt = v;
            d.dbl = v;
        
            printf("SPARC: float/double of %g\n", v);
            image_print(stdout, 0, f.data, sizeof(f.data));
            image_print(stdout, 0, d.data, sizeof(d.data));
            dump_float(f);
            dump_double(d);
        }
        
        
        int main(void)
        {
            print_value(+1.0);
            print_value(+2.0);
            print_value(+3.0);
            print_value( 0.0);
            print_value(-3.0);
            print_value(+3.1415926535897932);
            print_value(+1e126);
            return(0);
        }
        

        在 SUN UltraSPARC 上运行,我得到:

        SPARC: float/double of 1
        0x0000: 3F 80 00 00                                       ?...
        0x0000: 3F F0 00 00 00 00 00 00                           ?.......
        32-bit float: sign: 0, expt:  127 (unbiassed     0), mant:                0 (0x000000)
        64-bit float: sign: 0, expt: 1023 (unbiassed     0), mant:                0 (0x0000000000000)
        SPARC: float/double of 2
        0x0000: 40 00 00 00                                       @...
        0x0000: 40 00 00 00 00 00 00 00                           @.......
        32-bit float: sign: 0, expt:  128 (unbiassed     1), mant:                0 (0x000000)
        64-bit float: sign: 0, expt: 1024 (unbiassed     1), mant:                0 (0x0000000000000)
        SPARC: float/double of 3
        0x0000: 40 40 00 00                                       @@..
        0x0000: 40 08 00 00 00 00 00 00                           @.......
        32-bit float: sign: 0, expt:  128 (unbiassed     1), mant:          4194304 (0x400000)
        64-bit float: sign: 0, expt: 1024 (unbiassed     1), mant: 2251799813685248 (0x8000000000000)
        SPARC: float/double of 0
        0x0000: 00 00 00 00                                       ....
        0x0000: 00 00 00 00 00 00 00 00                           ........
        32-bit float: sign: 0, expt:    0 (unbiassed  -127), mant:                0 (0x000000)
        64-bit float: sign: 0, expt:    0 (unbiassed -1023), mant:                0 (0x0000000000000)
        SPARC: float/double of -3
        0x0000: C0 40 00 00                                       .@..
        0x0000: C0 08 00 00 00 00 00 00                           ........
        32-bit float: sign: 1, expt:  128 (unbiassed     1), mant:          4194304 (0x400000)
        64-bit float: sign: 1, expt: 1024 (unbiassed     1), mant: 2251799813685248 (0x8000000000000)
        SPARC: float/double of 3.14159
        0x0000: 40 49 0F DB                                       @I..
        0x0000: 40 09 21 FB 54 44 2D 18                           @.!.TD-.
        32-bit float: sign: 0, expt:  128 (unbiassed     1), mant:          4788187 (0x490FDB)
        64-bit float: sign: 0, expt: 1024 (unbiassed     1), mant: 2570638124657944 (0x921FB54442D18)
        SPARC: float/double of 1e+126
        0x0000: 7F 80 00 00                                       ....
        0x0000: 5A 17 A2 EC C4 14 A0 3F                           Z......?
        32-bit float: sign: 0, expt:  255 (unbiassed   128), mant:                0 (0x000000)
        64-bit float: sign: 0, expt: 1441 (unbiassed   418), mant:      -1005281217 (0xFFFFFFFFC414A03F)
        

        【讨论】:

          【解决方案6】:

          显然没有人愿意提及获得十六进制指数表示法是多么微不足道,所以这里是:

          #include <iostream>
          #include <cstdio>
          
          using namespace std;
          
          int main()
          {
              // C++11 manipulator
              cout << 23.0f << " : " << std::hexfloat << 23.0f << endl;
              // C equivalent solution
              printf("23.0 in hexadecimal is: %A\n", 23.0f);
          }
          

          【讨论】:

          • 这是 C++ 和 C 的正确方法。干得好!
          【解决方案7】:

          Float 和 Double 类(在 Java 中)都有一个 toHexString('float') 方法,所以这几乎可以用于十六进制转换

          Double.toHexString(42344);
          Float.toHexString(42344);
          

          简单易行!

          【讨论】:

          • 这个不错。 Double.doubleToLongBits 和 Float.floatToIntBits 更接近我正在寻找的表示。
          【解决方案8】:

          我不得不考虑在这里发布一段时间,因为这可能会激发其他程序员使用 C 做坏事。我还是决定发布它,但请记住:不要在没有适当文档的情况下将这种代码编写到任何严肃的应用程序中甚至三思而后行。

          抛开免责声明,我们开始吧。

          首先编写一个函数来打印例如二进制格式的长无符号变量:

          void printbin(unsigned long x, int n)
          {
            if (--n) printbin(x>>1, n);
            putchar("01"[x&1]);
          }
          

          不幸的是,我们不能直接使用这个函数来打印我们的浮点变量,所以我们不得不稍微修改一下。对于所有读过Carmack's Inverse Square Root Quake 技巧的人来说,这个 hack 可能看起来很熟悉。这个想法是为我们的浮点变量设置一个值,然后为我们的长整数变量获取相同的位掩码。因此,我们获取 f 的内存地址,将其转换为 long* 值,并使用该指针将 f 的位掩码作为 long 无符号。如果将此值打印为 long unsigned,结果将是一团糟,但这些位与原始浮点值中的位相同,因此并不重要。

          int main(void)
          {
            long unsigned lu;
            float f = -1.1f;
          
            lu = *(long*)&f;
            printbin(lu, 32);
            printf("\n");
            return 0;
          }
          

          如果您认为这种语法很糟糕,那么您是对的。

          【讨论】:

            【解决方案9】:

            在 Haskell 中,没有可访问的浮点的内部表示。但是您可以对多种格式进行二进制序列化,包括 Float 和 Double。以下解决方案适用于任何具有 Data.Binary 实例支持的类型:

            module BinarySerial where
            
            import Data.Bits
            import Data.Binary
            import qualified Data.ByteString.Lazy as B
            
            elemToBits :: (Bits a) => a -> [Bool]
            elemToBits a = map (testBit a) [0..7]
            
            listToBits :: (Bits a) => [a] -> [Bool]
            listToBits a = reverse $ concat $ map elemToBits a
            
            rawBits :: (Binary a) => a -> [Bool]
            rawBits a = listToBits $ B.unpack $ encode a
            

            可以使用 rawBits 进行转换:

            rawBits (3.14::Float)
            

            但是,如果您需要以这种方式访问​​浮点值,您可能做错了什么。真正的问题可能是如何访问浮点数的指数和有效位?答案是 exponent 和 Prelude 中的有效位:

            significand 3.14
            0.785
            
            exponent 3.14
            2
            

            【讨论】:

              【解决方案10】:

              Python:

              代码:

              import struct
              
              def float2bin(number, hexdecimal=False, single=False):
                  bytes = struct.pack('>f' if single else '>d', number)
                  func, length = (hex, 2) if hexdecimal else (bin, 8)
                  byte2bin = lambda byte: func(ord(byte))[2:].rjust(length, '0')
                  return ''.join(map(byte2bin, bytes))
              

              示例:

              >>> float2bin(1.0)
              '0011111111110000000000000000000000000000000000000000000000000000'
              >>> float2bin(-1.0)
              '1011111111110000000000000000000000000000000000000000000000000000'
              >>> float2bin(1.0, True)
              '3ff0000000000000'
              >>> float2bin(1.0, True, True)
              '3f800000'
              >>> float2bin(-1.0, True)
              'bff0000000000000'
              

              【讨论】:

              • 我想你想要rjust,不是吗?
              【解决方案11】:

              您可以在 C# 中使用这样的代码轻松地将 float 变量转换为 int 变量(或将 double 转换为 long):

              float f = ...;   
              unsafe
              {
                  int i = *(int*)&f;
              }
              

              【讨论】:

                【解决方案12】:

                为了将来参考,C++ 2a 引入了一个新的函数模板bit_cast 来完成这项工作。

                template< class To, class From >
                constexpr To bit_cast(const From& from) noexcept;
                

                我们可以简单地调用,

                float f = 3.14;
                std::bit_cast<int>(f);
                

                更多详情请见https://en.cppreference.com/w/cpp/numeric/bit_cast

                【讨论】:

                  【解决方案13】:

                  在 C++ 中,你可以用这种方式显示它的二进制表示:

                  template <class T>
                  std::bitset<sizeof(T)*8> binary_representation(const T& f)
                  {
                     typedef unsigned long TempType;
                     assert(sizeof(T)<=sizeof(TempType));
                     return std::bitset<sizeof(T)*8>(*(reinterpret_cast<const TempType*>(&f)));
                  }
                  

                  这里的限制是由于 bitset long 参数是无符号长的, 所以它可以浮动,您可以使用 bitset 和 extend 以外的其他东西 那个断言。

                  顺便说一句,cletus 建议在某种意义上是失败的,因为您需要“unsingned long long”来覆盖双精度,无论如何您需要显示二进制(1 或 0)表示的东西。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-10-10
                    • 2013-06-01
                    • 2017-06-16
                    • 2015-02-08
                    • 1970-01-01
                    相关资源
                    最近更新 更多