Как отобразить двоичное представление float или double?

Я хотел бы отобразить двоичное (или шестнадцатеричное) представление числа с плавающей запятой. Я знаю, как конвертировать вручную (используя метод здесь), но мне интересно видеть примеры кода, которые делают то же самое.

Хотя меня особенно интересуют решения на С++ и Java, я задаюсь вопросом, делают ли какие-либо языки особенно легкими, поэтому я делаю этот язык агностик. Мне бы хотелось увидеть некоторые решения на других языках.

EDIT: Я получил хорошее покрытие C, С++, С# и Java. Есть ли альтернативные языковые гуру, которые хотят добавить в список?

Ответ 1

C/С++ легко.

union ufloat {
  float f;
  unsigned u;
};

ufloat u1;
u1.f = 0.3f;

Затем вы просто выводите u1.u. Вы можете адаптировать эту реализацию.

Удваивает так же легко.

union udouble {
  double d;
  unsigned long u;
}

потому что удваивается 64 бит.

Java немного проще: используйте Float.floatToRawIntBits() в сочетании с Integer.toBinaryString() и Double.doubleToRawLongBits в сочетании с Long.toBinaryString().

Ответ 2

В C:

int fl = *(int*)&floatVar;

&floatVar получит адресную память, тогда (int*) будет указателем на эту адресную память, наконец, *, чтобы получить значение 4 байта с плавающей точкой в ​​int. Затем вы можете печатать двоичный формат или шестнадцатеричный формат.

Ответ 3

Java: поиск в Google находит эту ссылку на Форумы Sun

в частности (я сам этого не пробовал)

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

Ответ 4

В .NET(включая С#) у вас есть BitConverter, который принимает много типов, разрешая доступ к необработанному двоичному файлу; для получения hex, ToString("x2") является наиболее распространенным вариантом (возможно, завернутым в служебный метод):

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

Как ни странно, base-64 имеет однострочное преобразование (Convert.ToBase64String), но base-16 требует больше усилий. Если вы не обратитесь к Microsoft.VisualBasic, в этом случае:

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

Ответ 5

Я сделал это следующим образом:

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

Запуск на SUN UltraSPARC, я получил:

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)

Ответ 6

Хорошо, что оба класса Float и Double (в Java) имеют метод toHexString ('float'), поэтому в значительной степени это применимо для шестнадцатеричного преобразования

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

Просто как пирог!

Ответ 7

Мне пришлось подумать о том, чтобы публиковать здесь какое-то время, потому что это может вдохновить коллег-программистов на то, чтобы делать что-то плохое. Я решил опубликовать его в любом случае, но просто помню: не пишите этот код в какое-либо серьезное приложение без надлежащей документации и даже тогда думаю трижды.

С отклонением в сторону, здесь мы идем.

Сначала напишите функцию для печати, например, длинную неподписанную переменную в двоичном формате:

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

К сожалению, мы не можем напрямую использовать эту функцию для печати нашей переменной float, поэтому нам придется немного взломать. Взлом, вероятно, выглядит знакомым всем, кто читал об трюк Кармака Инверса Квадрата для Quake. Идея устанавливает значение для нашей переменной float, а затем получает ту же самую битовую маску для нашей длинной целочисленной переменной. Поэтому мы берем адрес памяти f, преобразуем его в длинное * значение и используем этот указатель, чтобы получить битовую маску f как длинный без знака. Если бы вы долгое время печатали это значение без знака, результат был бы бесполезным, но бит был таким же, как в исходном значении float, поэтому это не имеет особого значения.

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

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

Если вы считаете, что этот синтаксис ужасен, вы правы.

Ответ 8

В Haskell нет внутреннего представления доступных плавающих точек. Но вы можете выполнять двоичную сериализацию из многих форматов, включая Float и Double. Следующее решение является общим для любого типа, у которого есть экземпляр Data.Binary:

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

Преобразование может быть выполнено с помощью rawBits:

rawBits (3.14::Float)

Но, если вам нужно получить доступ к значению float таким образом, вы, вероятно, делаете что-то неправильно. Реальный вопрос: Как получить доступ к экспоненте и значению числа с плавающей запятой? Ответы exponent и значение из Prelude:

significand 3.14
0.785

exponent 3.14
2

Ответ 9

Python:

код:

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

Пример:

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

Ответ 10

По-видимому, никто не хотел упоминать, насколько тривиальным является получение шестнадцатеричного обозначения экспоненты, так что вот оно:

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

Ответ 11

Вы можете легко преобразовать переменную float в переменную int (или double to long), используя такой код в С#:

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

Ответ 12

В С++ вы можете показать двоичное представление таким образом:

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

предел здесь объясняется тем, что более длинный параметр bitet является unsigned long, поэтому он работает до float, вы можете использовать что-то еще, кроме битового набора, и расширение которые утверждают.

BTW, предложение cletus терпит неудачу в том смысле, что вам нужно "unsingned long long" для покрытия двойника, в любом случае вам нужно что-то, что отображает двоичное (1 или 0) представление.