¿Cómo se muestra la representación binaria de un flotante o doble?

Me gustaría mostrar la representación binaria (o hexadecimal) de un número de coma flotante. Sé cómo convertir a mano (usando el método aquí ), pero estoy interesado en ver ejemplos de código que hagan lo mismo.

Aunque estoy particularmente interesado en las soluciones C ++ y Java, me pregunto si algún idioma lo hace particularmente fácil, así que estoy haciendo que este lenguaje sea independiente . Me encantaría ver algunas soluciones en otros idiomas.

EDITAR: obtuve una buena cobertura de C, C ++, C # y Java. ¿Hay gurús de idiomas alternativos que quieran agregar a la lista?

C / C ++ es fácil.

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

Entonces acabas de enviar u1.u Puedes adaptar esta implementación .

Dobles igual de fácil.

 union udouble { double d; unsigned long u; } 

porque los dobles son 64 bit.

Java es un poco más fácil: use Float.floatToRawIntBits () combinado con Integer.toBinaryString () y Double.doubleToRawLongBits combinados con Long.toBinaryString () .

Cª:

 int fl = *(int*)&floatVar; 

&floatVar obtendría la memoria de dirección luego (int*) sería un puntero a esta memoria de dirección, finalmente el * para obtener el valor de la flotante de 4 bytes en int. Luego puede imprimir el formato binario o el formato hexadecimal.

En .NET (incluido C #), tiene BitConverter que acepta muchos tipos, lo que permite el acceso al binario sin formato; para obtener el hex, ToString("x2") es la opción más común (tal vez envuelta en un método de utilidad):

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

Curiosamente, base-64 tiene una conversión de 1 línea ( Convert.ToBase64String ), pero base-16 requiere más esfuerzo. A menos que haga referencia a Microsoft.VisualBasic, en cuyo caso:

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

Java: una búsqueda en google encuentra este enlace en los foros de Sun.

específicamente (no lo he probado yo mismo)

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

Lo hice de esta manera:

 /* @(#)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  #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); } 

Corriendo en un SUN UltraSPARC, obtuve:

 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) 

Bueno, tanto la clase Float como la clase Double (en Java) tienen un método toHexString (‘float’) por lo que esto haría con la conversión hexadecimal

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

¡Muy fácil!

Tuve que pensar en publicar aquí por un tiempo porque esto podría inspirar a codificadores compañeros para hacer cosas malas con C. Decidí publicarlo de todos modos, pero solo recuerde: no escriba este tipo de código en ninguna aplicación seria sin la documentación adecuada e incluso entonces piensa tres veces

Con el descargo de responsabilidad a un lado, aquí vamos.

Primero escriba una función para imprimir, por ejemplo, una variable larga sin signo en formato binario:

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

Lamentablemente, no podemos usar esta función directamente para imprimir nuestra variable flotante, así que tendremos que hackear un poco. Probablemente, el truco resulte familiar para todos los que hayan leído sobre el truco de Inverse Square Root de Carmack para Quake. La idea es establecer un valor para nuestra variable flotante y luego obtener la misma máscara de bits para nuestra variable entera larga. Entonces tomamos la dirección de memoria de f, la convertimos a un valor * largo y usamos ese puntero para obtener la máscara de bits de f como un signo sin signo largo. Si imprimiera este valor como largo sin firmar, el resultado sería un desastre, pero los bits son los mismos que en el valor de flotación original, por lo que realmente no importa.

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

Si crees que esta syntax es horrible, tienes razón.

En Haskell, no hay una representación interna de puntos flotantes accesible. Pero puedes hacer una serialización binaria desde muchos formatos, incluidos Float y Double. La siguiente solución es genérica para cualquier tipo que tenga instancia de Data.Binary compatible con:

 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 

La conversión se puede hacer con rawBits:

 rawBits (3.14::Float) 

Pero, si necesita acceder al valor flotante de esta manera, probablemente esté haciendo algo mal. La verdadera pregunta podría ser ¿Cómo acceder al exponente y al significado de un número de coma flotante? Las respuestas son exponente y significativo de Preludio:

 significand 3.14 0.785 exponent 3.14 2 

Pitón:

Código:

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

Muestra:

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

Aparentemente, a nadie le importó mencionar cuán trivial es obtener notación de exponente hexadecimal , así que aquí está:

 #include  #include  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); } 

Puede convertir fácilmente variable flotante a variable int (o doble a largo) usando dicho código en C #:

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

En C ++ puede mostrar la representación binaria de esta manera:

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

El límite aquí se debe al hecho de que el parámetro bitset longer es un unsigned long, por lo que funciona hasta flotar, puede usar algo más que bitset y la extensión que assert.

Por cierto, la sugerencia de Cletus falla en el sentido de que necesitas un “largo largo sin grabar” para cubrir un doble, de todos modos necesitas algo que muestre la representación binaria (1 o 0).

Para referencia futura, C ++ 2a introduce una nueva plantilla de función bit_cast que hace el trabajo.

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

Podemos simplemente llamar,

 float f = 3.14; std::bit_cast(f); 

Para obtener más detalles, consulte https://en.cppreference.com/w/cpp/numeric/bit_cast