我想显示浮点数的二进制(或十六进制)表示。我知道如何手动转换(使用这里的方法),但我有兴趣查看执行相同操作的代码示例。
虽然我对 C++ 和 Java 解决方案特别感兴趣,但我想知道是否有任何语言使它特别容易,所以我让这种语言不可知。我很想看到其他语言的一些解决方案。
编辑:我已经很好地覆盖了 C、C++、C# 和 Java。是否有任何替代语言专家想要添加到列表中?
我想显示浮点数的二进制(或十六进制)表示。我知道如何手动转换(使用这里的方法),但我有兴趣查看执行相同操作的代码示例。
虽然我对 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
将获得地址内存然后(int*)
将是指向该地址内存的指针,最后是 * 以获取 int 中浮点数的 4 个字节的值。然后你可以 printf 二进制格式或十六进制格式。
Java:谷歌搜索在Sun 的论坛上找到了这个链接
具体来说(我自己没有尝试过)
long binary = Double.doubleToLongBits(3.14159);
String strBinary = Long.toBinaryString(binary);
在 .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)\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)
显然没有人愿意提及获得十六进制指数符号是多么微不足道,所以这里是:
#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);
}
好吧 Float 和 Double 类(在 Java 中)都有一个 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]);
}
不幸的是,我们不能直接使用这个函数来打印我们的浮点变量,所以我们不得不稍微修改一下。对于读过Carmack 的Quake的 Inverse Square Root技巧的人来说,这个 hack 可能看起来很熟悉。这个想法是为我们的浮点变量设置一个值,然后为我们的长整数变量获取相同的位掩码。因此,我们获取 f 的内存地址,将其转换为 long* 值,并使用该指针将 f 的位掩码作为 long 无符号。如果您要将此值打印为无符号长,结果将是一团糟,但这些位与原始浮点值中的位相同,因此并不重要。
int main(void)
{
long unsigned lu;
float f = -1.1f;
lu = *(long*)&f;
printbin(lu, 32);
printf("\n");
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 变量转换为 int 变量(或将 double 转换为 long):
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)));
}
这里的限制是由于 bitset long 参数是 unsigned long 的事实,所以它可以使用 float,您可以使用 bitset 和扩展该断言之外的其他东西。
顺便说一句,cletus 建议在您需要“unsingned long long”来覆盖双精度的意义上失败了,无论如何您需要显示二进制(1 或 0)表示的东西。