English 中文(简体)
如何显示浮点数或双精度浮点数的二进制表示?
原标题:
  • 时间:2008-12-29 13:08:54
  •  标签:

我想展示一个浮点数的二进制(或十六进制)表示。我知道如何手动转换(使用此处的方法),但我对看到做同样事情的代码示例感兴趣。

尽管我特别对C++和Java解决方案感兴趣,但我想知道是否有任何语言使得它尤其容易,因此我会让它成为语言不依赖的。我很想看看其他语言的解决方案。

编辑:我已经涵盖了C、C++、C#和Java。还有其他语言大师想要添加到列表中吗?

最佳回答

C/C++很容易。

union ufloat {
  float f;
  unsigned u;
};

ufloat u1;
u1.f = 0.3f;

然后您只需输出u1.u

双打同样容易。

union udouble {
  double d;
  unsigned long u;
}

因为双精度是64位。

Java有点简单:使用 Float.floatToRawIntBits() 结合 Integer.toBinaryString()Double.doubleToRawLongBits 结合 Long.toBinaryString()

问题回答

在C语言中:

int fl = *(int*)&floatVar;

&floatVar would get the adress memory then (int*) would be a pointer to this adress memory, finally the * to get the value of the 4 bytes float in int. Then you can printf the binary format or hex format.

Java:谷歌搜索在Sun的论坛中找到了这个链接。

具體來說(我自己沒試過)

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

显然没有人在意如何获得十六进制指数表示法,所以这里给出:

#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
", 23.0f);
}

在.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);

我是這麼做的:

/*
@(#)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)
", 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)
", 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
", 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)

嗯,Java中的Float和Double类都有一个toHexString(float)方法,因此这基本上就足以进行十六进制转换。

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

易如反掌!

我不得不考虑在此发布一段时间,因为这可能会激励其他程序员用C进行恶意行为。我还是决定发帖,但请记住:不要在任何严肃的应用程序中编写此类代码,除非有适当的文档,即使有文档,也要三思而后行。

放下免责声明,我们开始吧。

首先编写一个函数,用于以二进制格式打印长无符号变量:

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

很不幸,我们无法直接使用这个函数来打印我们的浮点变量,所以我们需要做一些小修改。这个修改可能会让那些读过《卡马克的反平方根技巧》一文的人感到熟悉。思路是先给我们的浮点变量赋一个值,然后获取相同的位掩码,但是用长整型变量存储。因此我们获取f的内存地址,将其转换为一个长整型指针,并使用该指针获取f的位掩码,这个位掩码以长无符号整型的形式存在。如果您将这个值作为长无符号整型打印出来,结果会很混乱,但是位与原始的浮点值相同,所以并不重要。

int main(void)
{
  long unsigned lu;
  float f = -1.1f;

  lu = *(long*)&f;
  printbin(lu, 32);
  printf("
");
  return 0;
}

如果你认为这个语法很糟糕,那么你是正确的。

在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)

但是,如果您需要通过这种方式访问浮点值,则可能正在做一些错误的事情。真正的问题可能是如何访问浮点数的指数和有效数字?答案是Prelude中的指数和有效数字: 指数和有效数字。

significand 3.14
0.785

exponent 3.14
2

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 

在C#中,您可以使用以下代码轻松将浮点变量转换为整数变量(或将双精度转换为长整型):

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

为了将来参考,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

在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)));
}

the limit here is due the fact that bitset longer parameter is an unsigned long, so it works up to float, you can use something else than bitset and the extend that assert.

顺便提一下,Cletus的建议失败了,因为你需要一个“unsigned long long”来覆盖一个双精度数,无论如何,你需要一些能显示二进制(1或0)表示的东西。





相关问题
热门标签