Как отобразить двоичное представление с плавающей или двойной? - PullRequest
38 голосов
/ 29 декабря 2008

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

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

РЕДАКТИРОВАТЬ: Я получил хорошее освещение C, C ++, C # и Java. Есть ли какие-нибудь гуру альтернативного языка, которые хотят добавить в список?

Ответы [ 13 ]

31 голосов
/ 29 декабря 2008

C / 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 () * * тысяча двадцать-один.

25 голосов
/ 16 февраля 2012

В С:

int fl = *(int*)&floatVar;

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

7 голосов
/ 29 декабря 2008

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

конкретно (я сам не пробовал)

long binary = Double.doubleToLongBits(3.14159);
String strBinary = Long.toBinaryString(binary);
6 голосов
/ 29 декабря 2008

В .NET (включая C #) у вас есть BitConverter, который принимает много типов, предоставляя доступ к необработанному двоичному файлу; чтобы получить гекс, 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 имеет преобразование в 1 строку (Convert.ToBase64String), но base-16 требует больше усилий. Если вы не ссылаетесь на Microsoft.VisualBasic, в этом случае:

long tmp = BitConverter.DoubleToInt64Bits(123.45);
string hex = Microsoft.VisualBasic.Conversion.Hex(tmp);
4 голосов
/ 29 декабря 2008

Я сделал это так:

/*
@(#)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                                       .@..
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 голосов
/ 25 мая 2015

Очевидно, никто не удосужился упомянуть, как тривиально получить шестнадцатеричное представление показателя степени , поэтому вот оно:

#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 голосов
/ 29 августа 2012

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'
3 голосов
/ 21 ноября 2010

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

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

significand 3.14
0.785

exponent 3.14
2
3 голосов
/ 20 ноября 2010

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

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

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

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

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

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

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

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

3 голосов
/ 29 декабря 2008

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

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

Легко, как пирог!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...