it-swarm.com.de

Wie zeige ich die binäre Darstellung eines Floats oder Double an?

Ich möchte die binäre (oder hexadezimale) Darstellung einer Fließkommazahl anzeigen. Ich weiß, wie man von Hand konvertiert (mit der Methode hier ), aber ich bin daran interessiert, Codebeispiele zu sehen, die dasselbe tun.

Obwohl ich mich besonders für die C++ - und Java-Lösungen interessiere, frage ich mich, ob es eine Sprache besonders leicht macht, also mache ich diese sprachenunabhängig. Ich würde gerne Lösungen in anderen Sprachen sehen.

EDIT: Ich habe eine gute Abdeckung von C, C++, C # und Java erhalten. Gibt es alternative Gurus, die der Liste hinzugefügt werden möchten?

37
Bill the Lizard

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 diese Implementierung anpassen .

Doppelt genauso einfach.

union udouble {
  double d;
  unsigned long u;
}

weil doppelte sind 64 bit.

Java ist etwas einfacher: use Float.floatToRawIntBits () kombiniert mit Integer.toBinaryString () und Double.doubleToRawLongBits kombiniert mit Long.toBinaryString () .

31
cletus

In C: 

int fl = *(int*)&floatVar;

&floatVar würde den Adressspeicher erhalten, (int*) wäre ein Zeiger auf diesen Adressspeicher, und schließlich *, um den Wert des 4-Byte-Floats in int ..__ zu erhalten. Dann können Sie das binäre Format oder das Hex-Format drucken.

23
bslima

Java: Eine Google-Suche findet diesen Link in Suns Foren

speziell (ich habe es selbst nicht probiert)

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

In .NET (einschließlich C #) haben Sie BitConverter, das viele Typen akzeptiert und den Zugriff auf die Roh-Binärdatei ermöglicht. Um das Hex zu erhalten, ist ToString("x2") die am häufigsten verwendete Option (möglicherweise in eine Utility-Methode eingeschlossen):

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

Seltsamerweise hat base-64 eine 1-Zeilen-Konvertierung (Convert.ToBase64String), aber base-16 erfordert mehr Aufwand. Es sei denn, Sie verweisen auf Microsoft.VisualBasic. In diesem Fall gilt Folgendes:

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

Ich habe es so gemacht:

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

Ich lief auf einem Sun UltraSPARC und bekam:

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

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'
3
quantum

In Haskell ist keine interne Darstellung von Fließpunkten verfügbar. Sie können jedoch eine binäre Serialisierung aus vielen Formaten ausführen, einschließlich Float und Double. Die folgende Lösung ist generisch für jeden Typ, der eine Instanz von Data.Binary unterstützt:

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

Die Konvertierung kann mit rawBits erfolgen:

rawBits (3.14::Float)

Wenn Sie auf diese Weise auf den Float-Wert zugreifen müssen, machen Sie wahrscheinlich etwas falsch. Die eigentliche Frage könnte sein. Die Antworten sind Exponent und Signifikant von Prelude: 

significand 3.14
0.785

exponent 3.14
2
3
Zouppen

Nun, sowohl die Float- als auch die Double-Klasse (in Java) verfügen über eine toHexString ('float') -Methode, die so ziemlich für die Hex-Konvertierung geeignet wäre

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

Einfach wie ein Kuchen!

3
Gareth

Anscheinend interessierte es niemanden zu erwähnen, wie unbedeutend es ist, die hexadezimale Exponentennotation zu erhalten.

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

Ich musste eine Weile über das Posten hier nachdenken, weil dies andere Programmierer dazu anregen könnte, böse Dinge mit C zu tun. Ich entschied mich, es trotzdem zu posten. Denken Sie jedoch daran: Schreiben Sie diesen Code nicht in eine ernsthafte Anwendung ohne entsprechende Dokumentation denke dreimal.

Mit dem Disclaimer beiseite, geht es los.

Schreiben Sie zunächst eine Funktion, um zum Beispiel eine lange vorzeichenlose Variable im Binärformat zu drucken:

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

Leider können wir diese Funktion nicht direkt verwenden, um unsere Float-Variable zu drucken. Wir müssen also ein wenig hacken. Der Hack kommt wahrscheinlich allen bekannt vor, die über Carmacks Inverse Wurzelwurzel Trick für Quake gelesen haben. Die Idee ist ein Wert für unsere Float-Variable und dann die gleiche Bitmaske für unsere Long Integer-Variable. Also nehmen wir die Speicheradresse von f, konvertieren sie in einen long * -Wert und verwenden diesen Zeiger, um die Bitmaske von f als long unsigned zu erhalten. Wenn Sie diesen Wert als nicht vorzeichenbehaftet ausgeben würden, wäre das Ergebnis ein Durcheinander, aber die Bits sind dieselben wie beim ursprünglichen Gleitkommawert, sodass es nicht wirklich wichtig ist.

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

Sie können Float-Variable leicht in eine int-Variable konvertieren (oder doppelt zu lang), indem Sie solchen Code in C # verwenden: 

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

In C++ können Sie die binäre Darstellung folgendermaßen anzeigen:

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

der Grenzwert ist hier auf die Tatsache zurückzuführen, dass der Parameter bitset long ein vorzeichenloser long-Wert ist. Damit es funktioniert, können Sie etwas anderes als float verwenden. Sie können auch etwas anderes als bitset und das extend.

Übrigens scheitert der Cletus-Vorschlag in dem Sinne, dass Sie ein "unsingned long long" benötigen, um ein Double abzudecken.

0
Gaetano Mendola

Zur späteren Bezugnahme wird in C++ 2a eine neue Funktionsvorlage bit_cast eingeführt, die den Job ausführt.

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

Wir können einfach anrufen,

float f = 3.14;
std::bit_cast<int>(f);

Weitere Informationen finden Sie unter https://en.cppreference.com/w/cpp/numeric/bit_cast

0
JohnKoch