我该如何显示浮点数或双精度浮点数的二进制表示?

43
我想展示浮点数的二进制(或十六进制)表示。我知道如何手动转换(使用这里的方法),但我对看到执行相同操作的代码示例很感兴趣。
虽然我特别关注C ++和Java解决方案,但我想知道是否有任何语言使其特别容易,因此我将其设置为与语言无关。我希望能看到其他语言的解决方案。
编辑:我已经涵盖了C、C++、C#和Java。还有其他语言专家想要添加到列表中吗?
13个回答

34

3
注意!浮点数和双精度浮点数的位大小是标准化的,而长整型、整型等的大小取决于架构!你可以使用与实现相关的类型__int64和__int32(在Windows上)。 - xtofl
你可以使用我的 http://codepad.org/g8OtGEqX。然后,你可以使用 Integer<sizeof(double) * CHAR_BIT>::type 作为整数类型。我写这个是因为 D FAQ 声称 C++ 无法做到这一点(找到至少 N 位的类型)。 - Johannes Schaub - litb
代码非常保守地假设类型的大小(如果您将“long long”去掉,则可以在所有实现上运行)。我想人们可以编写一个更好的版本,其中包含每种基本类型的实际大小列表,并根据这些事实进行决策。大多数64位系统都具有64位长类型。 - Johannes Schaub - litb
8
这是未定义行为。不允许按顺序之外的方式访问共用体。 - Kerrek SB
1
@KerrekSB:这个问题被标记为与语言无关,访问联合成员而不是最后存储的成员是否被定义取决于语言。在C语言中,对于某些类型,行为是被定义的,对于其他类型(包括使用与大小相同或更小的无符号整数访问存储的双精度浮点数),行为是实现定义的,并且在某些情况下具有未定义的行为。 - Eric Postpischil
显示剩余3条评论

29
在C语言中:
int fl = *(int*)&floatVar;

&floatVar 获取的是内存地址,然后 (int*) 是一个指向这个地址的指针,最后使用 * 来获取 int 中 4 字节的 float 值。然后你可以使用 printf 打印二进制格式或十六进制格式。


7

显然,没有人提到如何轻松获得十六进制指数表示法,所以在这里:

#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都可以采用的正确方式。干得好! - Damian

7

Java:在谷歌搜索中,可以找到这个链接:Sun论坛

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

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

正是我所需要的。在发布问题之前,我在Google上搜索了如何在C++中实现这一点。我也应该寻找Java解决方案。 :) - Bill the Lizard
我本来期望 strBinary.length() 的值为64,但实际上却是63。有人知道为什么吗? - hiuller
同样地,Integer.toBinaryString(Float.floatToIntBits(floatNumber)); 将返回浮点数的字符串位表示。 - Rocky Inde

6
在.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有一个一行代码转换(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)

3

我考虑了一段时间是否要在这里发布,因为这可能会激励其他程序员用C做出一些恶意行为。不过我还是决定发出来,但请记住:如果没有适当的文档支持,甚至即使有也要三思而后行,不要将这种代码写入任何严肃的应用程序中。

除了免责声明,我们继续说下去。

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

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

不幸的是,我们不能直接使用这个函数来打印我们的浮点变量,所以我们需要进行一些小小的黑客。这个黑客可能对那些阅读过Carmack's Inverse Square Root Quake 技巧的人来说很熟悉。其思想是为我们的浮点变量设置一个值,然后获取相同的位掩码作为我们的长整型变量。因此,我们获取f的内存地址,将其转换为long*值,并使用该指针将f的位掩码作为长无符号数获取。如果您将此值打印为长无符号数,结果会很混乱,但位与原始浮点值相同,因此并不重要。

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

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

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

3
在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中的exponent和significand:

significand 3.14
0.785

exponent 3.14
2

3

在Java中,Float和Double类都有一个toHexString('float')方法,因此这基本上就可以用于十六进制转换。

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

简单易懂!

这很不错。Double.doubleToLongBits和Float.floatToIntBits更接近我要找的表示形式。 - Bill the Lizard

3

Python:

Code:

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 吗? - kjo

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