2008-12-29 8 views
37

Je souhaite afficher la représentation binaire (ou hexadécimale) d'un nombre à virgule flottante. Je sais convertir à la main (en utilisant la méthode here), mais je suis intéressé à voir des échantillons de code qui font la même chose.Comment afficher la représentation binaire d'un float ou d'un double?

Bien que je m'intéresse particulièrement aux solutions C++ et Java, je me demande si toutes les langues le rendent particulièrement facile, donc je fais ce langage agnostique. J'aimerais voir des solutions dans d'autres langues.

EDIT: J'ai une bonne couverture de C, C++, C# et Java. Y a-t-il des gourous de langue alternative qui veulent ajouter à la liste?

Répondre

30

C/C++ est facile.

union ufloat { 
    float f; 
    unsigned u; 
}; 

ufloat u1; 
u1.f = 0.3f; 

Ensuite, vous venez de sortir u1.u. Vous pouvez adapter this implementation.

Double tout aussi facile.

union udouble { 
    double d; 
    unsigned long u; 
} 

parce que les doubles sont de 64 bits.

Java est un peu plus facile: utiliser Float.floatToRawIntBits() combiné avec Integer.toBinaryString() et Double.doubleToRawLongBits combiné avec Long.toBinaryString().

+3

esprit! Les tailles de bit des flotteurs et des doubles sont standardisées, les tailles des longs, des ints etc ... dépendent de l'architecture! Vous pouvez utiliser les types spécifiques à l'implémentation __int64 et __int32 (sous Windows). – xtofl

+0

vous pouvez utiliser mon http://codepad.org/g8OtGEqX. alors vous pouvez utiliser Integer :: type comme type entier à la place. Je l'ai écrit parce que le D faq revendiqué C++ ne peut pas le faire (trouver un type avec au moins N bits) –

+0

le code suppose la taille des types de manière très conservatrice (donc cela fonctionne sur toutes les implémentations si vous prenez le "long long"). Je suppose que l'on pourrait écrire une meilleure version qui a une liste des tailles réelles de chaque type de base et décide de ces faits. La plupart des systèmes 64 bits ont des types 64 bits. –

6

Java: une recherche google trouve sur ce lien Sun's forums

spécifiquement (je ne l'ai pas essayé moi-même)

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

Exactement ce que je cherchais. J'ai cherché sur Google comment faire cela en C++ avant de poster la question. J'aurais dû chercher des solutions Java aussi. :) –

+0

Je m'attendrais à ce que 'strBinary.length()' soit à 64 mais j'obtiens 63 à la place. Est-ce que quelqu'un sait pourquoi? – hiuller

+0

De même, 'Integer.toBinaryString (Float.floatToIntBits (floatNumber));' retournerait la représentation de bit de chaîne pour les flottants. –

6

Dans .NET (y compris C#), vous avez BitConverter qui accepte de nombreux types , permettant l'accès au binaire brut; pour obtenir l'hexagone, ToString("x2") est l'option la plus courante (peut-être enveloppé dans un procédé d'utilité):

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, base 64 a une conversion de 1-ligne (Convert.ToBase64String), mais base 16 prend plus d'effort. À moins que vous faites référence à Microsoft.VisualBasic, auquel cas:

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

bien à la fois le flotteur et la classe Double (en Java) ont une méthode toHexString (« float ») si à peu près ce ferait pour la conversion hexagonale

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

Facile comme tarte!

+0

Je suis un programmeur paresseux – Gareth

+0

C'est assez bon. Double.doubleToLongBits et Float.floatToIntBits sont plus proches de la représentation que je recherche. –

0

En C++, vous pouvez montrer 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 BITSET paramètre plus est un long, non signé de sorte qu'il fonctionne à flotter, vous pouvez utiliser autre chose que bitset et étendre qui affirme. BTW, suggestion de claquement échoue dans le sens où vous avez besoin d'un "long long unsingned" pour couvrir un double, de toute façon vous avez besoin de quelque chose qui montre la représentation binaire (1 ou 0).

4

je l'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); 
} 

Fonctionnant sur un Sun UltraSPARC, je suis arrivé:

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          [email protected] 
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

je devais penser à l'affichage ici pendant un certain temps parce que cela pourrait inspirer d'autres codeurs à faire des choses mauvaises avec C. J'ai décidé de poster quand même, mais rappelez-vous juste: n'écrivez pas ce genre de code pour une application sérieuse sans documentation appropriée et même alors, pensez trois fois.

Avec l'exclusion de côté, nous y voilà.

d'abord écrire une fonction pour l'impression par exemple une variable de type long non signé au format binaire:

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

Malheureusement nous ne pouvons pas utiliser directement cette fonction pour imprimer notre variable float donc nous allons devoir pirater un peu . Le hack semble probablement familier à tous ceux qui ont lu sur Carmack's Inverse Square Root astuce pour Quake. L'idée est de définir une valeur pour notre variable float et ensuite obtenir le même masque de bits pour notre variable d'entier long. Donc, nous prenons l'adresse mémoire de f, la convertissons en une valeur longue * et utilisons ce pointeur pour obtenir le masque de bits de f comme un long non signé. Si vous deviez imprimer cette valeur comme non signée longtemps, le résultat serait un désordre, mais les bits sont les mêmes que dans la valeur flottante d'origine, donc cela 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 horrible, vous avez raison.

3

Dans Haskell, il n'y a pas de représentation interne des points flottants accessibles. Mais vous pouvez faire une sérialisation binaire à partir de nombreux formats, y compris Float et Double. La solution suivante est générique à tout type qui a instance de Data.Binary soutient:

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 conversion peut se faire avec rawBits:

rawBits (3.14::Float) 

Mais, si vous avez besoin d'accéder à la valeur flottante de cette façon , vous faites probablement quelque chose de mal. La vraie question pourrait être Comment accéder à l'exposant et à la signification d'un nombre à virgule flottante? Les réponses sont exponent et significand de Prélude:

significand 3.14 
0.785 

exponent 3.14 
2 
21

En C:

int fl = *(int*)&floatVar; 

&floatVar obtiendraient la mémoire adresse alors (int*) serait un pointeur vers cette mémoire adresse, enfin le * pour obtenir la valeur du flottant de 4 octets dans int. Ensuite, vous pouvez imprimer le format binaire ou le format hexadécimal.

+1

Simple et élégant. – Lolo

2

Vous pouvez facilement convertir variable float en int variable (ou double à long) en utilisant ce code en C#:

float f = ...; 
unsafe 
{ 
    int i = *(int*)&f; 
} 
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:].ljust(length, '0') 
    return ''.join(map(byte2bin, bytes)) 

échantillon :

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

Je pense que vous voulez 'rjust' là, non? – kjo

3

Apparemment, personne ne se souciait de mentionner à quel point trivial est d'obtenir notation exposant hexadécimal, donc la voici:

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

C'est la bonne façon d'aller à la fois pour C++ et C. Bravo! – Damian