43

I'd like to display the binary (or hexadecimal) representation of a floating point number. I know how to convert by hand (using the method here), but I'm interested in seeing code samples that do the same.

Although I'm particularly interested in the C++ and Java solutions, I wonder if any languages make it particularly easy so I'm making this language agnostic. I'd love to see some solutions in other languages.

EDIT: I've gotten good coverage of C, C++, C#, and Java. Are there any alternative-language gurus out there who want to add to the list?

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880

13 Answers13

34

C/C++ is easy.

union ufloat {
  float f;
  unsigned u;
};

ufloat u1;
u1.f = 0.3f;

Then you just output u1.u.

Doubles just as easy.

union udouble {
  double d;
  unsigned long u;
}

because doubles are 64 bit.

Java is a bit easier: use Float.floatToRawIntBits() combined with Integer.toBinaryString() and Double.doubleToRawLongBits combined with Long.toBinaryString().

Dimitri Lesnoff
  • 317
  • 1
  • 14
cletus
  • 616,129
  • 168
  • 910
  • 942
  • 3
    Mind! The bit sizes of floats and doubles are standardized, the sizes of longs, ints etc... depend on the architecture! You can use implemenetation-specific types __int64 and __int32 (on windows). – xtofl Dec 29 '08 at 13:18
  • you can use my http://codepad.org/g8OtGEqX . then you can use Integer< sizeof(double) * CHAR_BIT >::type as the integer type instead. i wrote it because the D faq claimed C++ can't do that (find a type with at least N bits) – Johannes Schaub - litb Dec 29 '08 at 14:02
  • the code assumes the size of types very conservatively (so it works on all implementations if you take the "long long" out). i suppose one could write a better version that has a list of the actual sizes of each basic type and decides on those facts. most 64 bit systems have 64bit long types. – Johannes Schaub - litb Dec 29 '08 at 14:04
  • I guess the first comment is that sizes are NOT standarized, and in fact they do depend on the architecture. The standard only warrants that sizeof(char)<=sizeof(short)<=sizeof(int)<=sizeof(long)<=sizeof(long long), but they could all be just 8 bits. (I am not sure but char could be defined as 8bit) – David Rodríguez - dribeas Dec 29 '08 at 14:38
  • @dribeas: char is defined as 1 *byte*, which may not actually be 8 bits (I think it's guaranteed to be at least 8 bits, could be more. Or I could be wrong.). But you are always, always, always guaranteed in C/C++ that sizeof(char) == 1 – Jon Dec 29 '08 at 15:04
  • 7
    This is undefined behaviour. Accessing a union out of order is not allowed. – Kerrek SB Nov 25 '11 at 18:00
  • 1
    @KerrekSB: This question is tagged language-agnostic, and whether accessing a union member other than the last-stored member is defined is language-dependent. In C, the behavior is defined for certain types, is implementation-defined for others (including accessing a stored double with unsigned integers of the same or lesser size), and has undefined behavior in certain circumstances. – Eric Postpischil May 09 '13 at 15:19
  • The link probably doesn't link to the right page anymore. It does not explain why it works. This is undefined behaviour as stated above, but is implemented as a language extension by many compilers according to https://en.cppreference.com/w/cpp/language/union . Which compilers support this ? – Dimitri Lesnoff Dec 16 '22 at 10:15
29

In C:

int fl = *(int*)&floatVar;

&floatVar would get the adress memory then (int*) would be a pointer to this adress memory, finally the * to get the value of the 4 bytes float in int. Then you can printf the binary format or hex format.

bslima
  • 1,097
  • 2
  • 15
  • 17
7

Apparently nobody cared to mention how trivial is to obtain hexadecimal exponent notation, so here it is:

#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);
}
Massimiliano
  • 7,842
  • 2
  • 47
  • 62
7

Java: a google search finds this link on Sun's forums

specifically (I haven't tried this myself)

long binary = Double.doubleToLongBits(3.14159);
String strBinary = Long.toBinaryString(binary);
Pang
  • 9,564
  • 146
  • 81
  • 122
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • Exactly what I was looking for. I searched Google for how to do this in C++ before I posted the question. I should have looked for Java solutions too. :) – Bill the Lizard Dec 29 '08 at 15:25
  • I would expect `strBinary.length()` to be 64 but I get 63 instead. Does anyone knows why? – hiuller Jun 16 '14 at 00:06
  • Likewise, `Integer.toBinaryString(Float.floatToIntBits(floatNumber));` would return the string bit representation for floats. – Rocky Inde Apr 13 '16 at 04:30
6

In .NET (including C#), you have BitConverter that accepts many types, allowing access to the raw binary; to get the hex, ToString("x2") is the most common option (perhaps wrapped in a utility method):

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

Oddly, base-64 has a 1-line conversion (Convert.ToBase64String), but base-16 takes more effort. Unless you reference Microsoft.VisualBasic, in which case:

long tmp = BitConverter.DoubleToInt64Bits(123.45);
string hex = Microsoft.VisualBasic.Conversion.Hex(tmp);
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
5

I did it this way:

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

Running on a SUN UltraSPARC, I got:

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)
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
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:].rjust(length, '0')
    return ''.join(map(byte2bin, bytes))

Sample:

>>> float2bin(1.0)
'0011111111110000000000000000000000000000000000000000000000000000'
>>> float2bin(-1.0)
'1011111111110000000000000000000000000000000000000000000000000000'
>>> float2bin(1.0, True)
'3ff0000000000000'
>>> float2bin(1.0, True, True)
'3f800000'
>>> float2bin(-1.0, True)
'bff0000000000000'
Tanner Swett
  • 3,241
  • 1
  • 26
  • 32
quantum
  • 3,672
  • 29
  • 51
3

Well both the Float and Double class (in Java) have a toHexString('float') method so pretty much this would do for hex conversion

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

Easy as pie!

Gareth
  • 2,180
  • 5
  • 19
  • 24
3

I had to think about posting here for a while because this might inspire fellow coders to do evil things with C. I decided to post it anyway but just remember: do not write this kind of code to any serious application without proper documentation and even then think thrice.

With the disclaimer aside, here we go.

First write a function for printing for instance a long unsigned variable in binary format:

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

Unfortunately we can't directly use this function to print our float variable so we'll have to hack a little. The hack probably looks familiar to everyone who have read about Carmack's Inverse Square Root trick for Quake. The idea is set a value for our float variable and then get the same bit mask for our long integer variable. So we take the memory address of f, convert it to a long* value and use that pointer to get the bit mask of f as a long unsigned. If you were to print this value as long unsigned, the result would be a mess, but the bits are the same as in the original float value so it doesn't really matter.

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

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

If you think this syntax is awful, you're right.

Ville Salonen
  • 2,654
  • 4
  • 29
  • 34
3

In Haskell, there is no internal representation of floating points accessible. But you can do binary serialiazation from many formats, including Float and Double. The following solution is generic to any type that has instance of Data.Binary supports:

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 can be done with rawBits:

rawBits (3.14::Float)

But, if you need to access the float value this way, you are probably doing something wrong. The real question might be How to access exponent and significand of a floating-point number? The answers are exponent and significand from Prelude:

significand 3.14
0.785

exponent 3.14
2
Zouppen
  • 1,214
  • 11
  • 17
2

You can easy convert float variable to int variable (or double to long) using such code in C#:

float f = ...;   
unsafe
{
    int i = *(int*)&f;
}
Ivan Kochurkin
  • 4,413
  • 8
  • 45
  • 80
1

For future reference, C++ 2a introduce a new function template bit_cast that does the job.

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

We can simply call,

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

For more details, see https://en.cppreference.com/w/cpp/numeric/bit_cast

MaxPlankton
  • 1,158
  • 14
  • 28
0

In C++ you can show the binary representation it in this way:

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

the limit here is due the fact that bitset longer parameter is an unsigned long, so it works up to float, you can use something else than bitset and the extend that assert.

BTW, cletus suggestion fails in the sense that you need an "unsingned long long" to cover a double, anyway you need something that shows the binary (1 or 0) representation.

Gaetano Mendola
  • 1,344
  • 3
  • 12
  • 27