2008-12-29 11 views
37

Ich möchte die binäre (oder hexadezimale) Darstellung einer Fließkommazahl anzeigen. Ich weiß, wie man mit der Hand konvertiert (mit der Methode here), aber ich bin daran interessiert, Code-Beispiele zu sehen, die das gleiche tun.Wie zeige ich die Binärdarstellung eines Floats oder Double an?

Obwohl ich besonders an den C++ - und Java-Lösungen interessiert bin, frage ich mich, ob irgendwelche Sprachen es besonders einfach machen, also mache ich diese Sprache agnostic. Ich würde gerne einige Lösungen in anderen Sprachen sehen.

EDIT: Ich habe gute Abdeckung von C, C++, C# und Java bekommen. Gibt es alternative Sprach-Gurus, die zur Liste hinzufügen möchten?

Antwort

30

C/C++ ist einfach.

union ufloat { 
    float f; 
    unsigned u; 
}; 

ufloat u1; 
u1.f = 0.3f; 

Dann geben Sie einfach u1.u aus. Sie können this implementation anpassen.

Doppelt genauso einfach.

union udouble { 
    double d; 
    unsigned long u; 
} 

weil Doppel 64 Bit sind.

Java ist ein bisschen einfacher: Verwenden Sie Float.floatToRawIntBits() kombiniert mit Integer.toBinaryString() und Double.doubleToRawLongBits mit Long.toBinaryString() kombiniert.

+3

Verstand! Die Bitgrößen von Floats und Doubles sind standardisiert, die Größe von Longs, Ints etc ... hängt von der Architektur ab! Sie können die implementierungsspezifischen Typen __int64 und __int32 (für Windows) verwenden. – xtofl

+0

können Sie meine http://codepad.org/g8OtGEqX verwenden. Dann können Sie Integer :: als Integer-Typ verwenden. Ich habe es geschrieben, weil die D faq behauptete, dass C++ das nicht kann (finde einen Typ mit mindestens N Bits) –

+0

der Code nimmt die Größe der Typen sehr konservativ an (so funktioniert es bei allen Implementierungen, wenn man das "long long" herausnimmt). Ich nehme an, man könnte eine bessere Version schreiben, die eine Liste der tatsächlichen Größen jedes Grundtyps enthält und über diese Fakten entscheidet. Die meisten 64-Bit-Systeme haben 64-Bit-lange Typen. –

6

Java: eine Google-Suche findet auf diesen Link auf Sun's forums

speziell (ich das selbst noch nicht ausprobiert)

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

Genau das, was ich gesucht habe. Ich habe Google danach gesucht, wie man das in C++ macht, bevor ich die Frage gepostet habe. Ich hätte auch nach Java-Lösungen suchen sollen. :) –

+0

Ich würde 'strBinary.length()' erwarten, 64 zu sein, aber ich bekomme 63 stattdessen. Weiß jemand warum? – hiuller

+0

In gleicher Weise würde 'Integer.toBinaryString (Float.floatToIntBits (floatNumber));' 'die String-Bit-Repräsentation für Floats zurückgeben. –

6

In .NET (einschließlich C#), haben Sie BitConverter, die viele Arten akzeptiert Erlauben des Zugriffs auf die rohe Binärdatei; die Hex zu bekommen, ist ToString("x2") die häufigste Option (vielleicht in einer Dienstprogramm Methode verpackt):

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

Merkwürdig ist, dass base-64 hat eine 1-line-Umwandlung (Convert.ToBase64String), aber Basis 16 braucht mehr Mühe. Es sei denn, Sie Microsoft.VisualBasic, wobei in diesem Fall verweisen:

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

Nun sowohl das Float und Double-Klasse (in Java) einen toHexString ('float') Methode so ziemlich dies für hex Umwandlung tun würde

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

Einfach wie Kuchen!

+0

Ich bin so ein fauler Programmierer – Gareth

+0

Das ist ziemlich gut. Double.doubleToLongBits und Float.floatToIntBits sind näher an der Repräsentation, die ich suche. –

0

In C++ Sie die binäre Darstellung auf diese Weise zeigen können:

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

die Grenze hier ist aufgrund der Tatsache, dass mehr Parameter ein unsigned long bitset, so funktioniert es zu schwimmen, können Sie Verwenden Sie etwas anderes als Bitset und die Erweiterung , die behaupten.

BTW, schlägt Cletus Vorschlag in dem Sinne, dass Sie eine "unsinned long long" benötigen, um ein double abzudecken, sowieso brauchen Sie etwas, das die binäre Darstellung (1 oder 0) zeigt.

4

ich es auf diese Weise tat:

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

Laufen auf einem Sun Ultrasparc, ich habe:

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

ich Entsendung hier für eine Weile zu denken hatte, weil dieser Kerl Programmierer inspirieren könnte Mach böse Dinge mit C. Ich habe mich dafür entschieden, es trotzdem zu posten, aber denk dran: Schreibe diese Art von Code nicht in eine ernsthafte Anwendung ohne die richtige Dokumentation und denke selbst dann dreimal.

Mit dem Disclaimer beiseite, hier gehen wir.

Erste eine Funktion schreiben, für das Drucken zum Beispiel eine lange unsigned Variable im Binärformat:

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

können wir leider nicht direkt diese Funktion nutzen unsere float-Variable zu drucken, so werden wir ein wenig hacken haben . Der Hack kommt allen bekannt vor, die über Carmack's Inverse Square Root Trick für Quake gelesen haben. Die Idee ist ein Wert für unsere float-Variable und dann erhalten Sie die gleiche Bitmaske für unsere Long-Integer-Variable. Also nehmen wir die Speicheradresse von f, wandeln sie in einen long * -Wert um und verwenden diesen Zeiger, um die Bitmaske von f als lang vorzeichenlos zu erhalten. Wenn Sie diesen Wert so lange ohne Vorzeichen ausgeben würden, wäre das Ergebnis ein Chaos, aber die Bits sind die gleichen wie im ursprünglichen Gleitkommawert, so dass es eigentlich keine Rolle spielt.

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

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

Wenn Sie denken, dass diese Syntax schrecklich ist, haben Sie Recht.

3

In Haskell gibt es keine interne Darstellung von Gleitpunkten. Aber Sie können binäre Serialisierung aus vielen Formaten, einschließlich Float und Double. Die folgende Lösung ist auf jede Art generischer, die Instanz von Data.Binary unterstützt hat:

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 

Konvertierung mit rawBits getan werden kann:

rawBits (3.14::Float) 

Aber, wenn Sie den Schwimmer Wert auf diese Weise zugreifen müssen Wahrscheinlich machst du etwas falsch. Die wirkliche Frage könnte sein Wie man auf den Exponenten und den Signifikanten einer Fließkommazahl zugreift? Die Antworten sind exponent und Signifikanden von Prelude:

significand 3.14 
0.785 

exponent 3.14 
2 
21

In C:

int fl = *(int*)&floatVar; 

&floatVar die Adresse Speicher erhalten würde dann (int*) ein Zeiger auf diese Adresse Erinnerung sein würde, schließlich die * die erhalten Wert der 4 Bytes float in int. Dann können Sie das Binärformat oder das Hexadezimalformat drucken.

+1

Einfach und elegant. – Lolo

2

Sie können einfach float-Variable umwandeln Variable int (oder doppelt zu lang) unter Verwendung eines solchen Code in 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)) 

Probe :

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

Ich denke du willst 'rjust' dort, nein? – kjo

3

Offenbar sorgte niemand zu erwähnen, wie trivial ist hexadezimale Notation Exponenten zu erhalten, so ist es hier:

#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

Dies ist der richtige Weg für C++ und C. Gut gemacht! – Damian