Comment puis-je afficher la représentation binaire d'un float ou double?
j'aimerais afficher la représentation binaire (ou hexadécimale) d'un nombre à virgule flottante. Je sais comment faire pour convertir à la main (à l'aide de la méthode ici), mais je suis intéressé à voir des échantillons de code qui font la même chose.
bien que je sois particulièrement intéressé par les solutions C++ et Java, je me demande si certains langages rendent cela particulièrement facile alors je fais ceci langue agnostique. J'aimerais voir quelques solutions dans d'autres langue.
EDIT: j'ai obtenu une bonne couverture de C, C++, C#, et Java. Il y a un autre-langue des gourous qui veulent ajouter à la liste?
13 réponses
C/c++ est facile.
union ufloat {
float f;
unsigned u;
};
ufloat u1;
u1.f = 0.3f;
alors vous venez de sortir u1.u
. Vous pouvez adapter cette mise en œuvre.
Double tout aussi facile.
union udouble {
double d;
unsigned long u;
}
parce que les doubles sont 64 bits.
Java est un peu plus facile: utiliser Float.floatToRawIntBits () combinés avec Entier.toBinaryString() et Double.doubleToRawLongBits combinés avec Long.toBinaryString().
EN C:
int fl = *(int*)&floatVar;
&floatVar
obtiendrait la mémoire d'adresse alors (int*)
serait un pointeur vers cette mémoire d'adresse, finalement le * pour obtenir la valeur des 4 octets flottants en int.
Ensuite, vous pouvez imprimer le format binaire ou le format hex.
.NET (notamment en C#), vous avez BitConverter
qui accepte de nombreux types, permettant l'accès au binaire brut; pour obtenir l'hexadécimal,ToString("x2")
est l'option la plus courante (peut-être enveloppé dans une méthode utilitaire):
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);
curieusement, la base-64 a une conversion d'une ligne (Convert.ToBase64String
), mais la base-16 demande plus d'effort. Sauf si vous faites référence à Microsoft.VisualBasic, auquel cas:
long tmp = BitConverter.DoubleToInt64Bits(123.45);
string hex = Microsoft.VisualBasic.Conversion.Hex(tmp);
Java: une recherche google trouve ce lien sur forums de Sun
spécifiquement (je n'ai pas essayé moi-même)
long binary = Double.doubleToLongBits(3.14159);
String strBinary = Long.toBinaryString(binary);
j'ai fait de cette façon:
/*
@(#)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);
}
en cours d'Exécution sur une station SUN UltraSPARC, j'ai:
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)
Eh bien les deux classes Float et Double (en Java) ont une méthode toHexString ('float') si bien que cela ferait pour la conversion hex
Double.toHexString(42344);
Float.toHexString(42344);
facile comme de la tarte!
j'ai dû penser à poster ici pendant un certain temps parce que cela pourrait inspirer d'autres codeurs de faire des choses mauvaises avec C. j'ai décidé de le poster de toute façon mais juste se rappeler: Ne pas écrire ce genre de code à une application sérieuse sans la documentation appropriée et même alors penser trois fois.
avec le disclaimer mis à part, nous y voilà.
Ecrire D'abord une fonction pour imprimer par exemple une longue variable non signée en format binaire:
void printbin(unsigned long x, int n)
{
if (--n) printbin(x>>1, n);
putchar("01"[x&1]);
}
Malheureusement nous ne pouvons pas utilisez directement cette fonction pour imprimer notre variable float de sorte que nous aurons à pirater un peu. Le hack semble probablement familier à tous ceux qui ont lu à propos de racine carrée Inverse de Carmack truc de Quake. L'idée est de définir une valeur pour notre variable float et ensuite d'obtenir le même masque de bits pour notre variable entière longue. Nous prenons donc l'adresse mémoire de f, la convertissons en une valeur long* et utilisons ce pointeur pour obtenir le masque de bits de f comme un long non signé. Si vous deviez imprimer cette valeur tant non signé, le résultat serait un gâchis, mais les bits sont les mêmes que dans la valeur float originale donc il n'a pas vraiment d'importance.
int main(void)
{
long unsigned lu;
float f = -1.1f;
lu = *(long*)&f;
printbin(lu, 32);
printf("\n");
return 0;
}
Si vous pensez que cette syntaxe est terrible, vous avez raison.
à Haskell, il n'y a pas de représentation interne de points flottants accessibles. Mais vous pouvez faire serialiazation binaire à partir de nombreux formats, y compris Float et Double. La solution suivante est générique pour tout type d'instance de Données.Binaire prend en charge:
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
Conversion peut être fait avec rawBits:
rawBits (3.14::Float)
mais, si vous avez besoin d'accéder à la valeur float de cette façon, vous faites probablement quelque chose de mal. La vraie question pourrait être comment accéder exposant et significatif d'un nombre de virgule flottante? Les réponses sont exposant et significande de Prélude:
significand 3.14
0.785
exponent 3.14
2
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:].ljust(length, '0')
return ''.join(map(byte2bin, bytes))
Exemple:
>>> float2bin(1.0)
'1111110011110000000000000000000000000000000000000000000000000000'
>>> float2bin(-1.0)
'1011111111110000000000000000000000000000000000000000000000000000'
>>> float2bin(1.0, True)
'3ff0000000000000'
>>> float2bin(1.0, True, True)
'3f800000'
>>> float2bin(-1.0, True)
'bff0000000000000'
apparemment personne ne s'est soucié de mentionner combien trivial est d'obtenir hexadécimal exposant notation, si elle est ici:
#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);
}
Vous pouvez facilement convertir la variable float en variable int (ou double à long) en utilisant un tel code en C#:
float f = ...;
unsafe
{
int i = *(int*)&f;
}
En C++, vous pouvez afficher la représentation binaire de cette manière:
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)));
}
la limite ici est due au fait que le paramètre bitset longer est un long non signé, donc, il fonctionne jusqu'à flotter, vous pouvez utiliser autre chose que bitset et l'extension que faire valoir.
BTW, la suggestion de cletus échoue dans le sens où vous avez besoin d'un "unsingned long" pour couvrir un double, de toute façon vous avez besoin de quelque chose qui montre la représentation binaire (1 ou 0).
Pour référence future, C++ 2a introduire un nouveau modèle de fonction bit_cast
ça fait l'affaire.
template< class To, class From >
constexpr To bit_cast(const From& from) noexcept;
il suffit de l'appeler,
float f = 3.14;
std::bit_cast<int>(f);
pour plus de détails, voir https://en.cppreference.com/w/cpp/numeric/bit_cast