Перейдите в двоичный файл в C ++ - PullRequest
4 голосов
/ 30 апреля 2010

Мне интересно, есть ли способ представить число с помощью char в C ++?

Например:

int main()  
{  
    float test = 4.7567;  
    char result = charRepresentation(test);  
    return 0;  
}  

Я читал, что, возможно, используя bitset, я могу это сделать, но я не совсем уверен.

Предположим, что моя переменная с плавающей запятой - 01001010 01001010 01001010 01001010 в двоичном формате.

Если я хочу массив символов из 4 элементов, первый элемент будет 01001010, второй: 01001010 и т. Д.

Могу ли я представить переменную с плавающей точкой в ​​массиве char из 4 элементов?

Ответы [ 7 ]

8 голосов
/ 30 апреля 2010

Я подозреваю, что вы пытаетесь сказать:

int main()  
{  
    float test = 4.7567; 
    char result[sizeof(float)];

    memcpy(result, &test, sizeof(test));

    /* now result is storing the float,
           but you can treat it as an array of 
           arbitrary chars

       for example:
    */
    for (int n = 0; n < sizeof(float); ++n) 
        printf("%x", result[n]);

    return 0;  
}  

Отредактировано, чтобы добавить : все люди, указывающие, что вы не можете вписать float в 8биты, конечно, верны, но на самом деле OP нащупывает понимание того, что float, как и все атомарные типы данных, в конечном итоге является простым непрерывным блоком байтов.Это не очевидно для всех новичков.

2 голосов
/ 30 апреля 2010

с помощью штуцера - это просто и легко

union
{
  float f;
  unsigned int ul;
  unsigned char uc[4];
} myfloatun;

myfloatun.f=somenum;
printf("0x%08X\n",myfloatun.ul);

Гораздо безопаснее с точки зрения компилятора, чем указатели. Memcpy тоже отлично работает.

EDIT

Хорошо, хорошо, вот полностью функциональные примеры. Да, вы должны использовать объединения с осторожностью, если вы не следите за тем, как этот компилятор распределяет объединение и блокирует или выравнивает его, который он может сломать, и поэтому некоторые / многие говорят, что опасно использовать объединения таким способом. И все же альтернативы считаются безопасными?

У некоторого чтения C ++ есть свои проблемы с объединениями, и объединение вполне может просто не сработать. Если вы действительно имели в виду C ++, а не C, то это, вероятно, плохо. Если вы сказали «Клинекс» и имели в виду ткани, это может сработать.


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef union
{
    float f;
    unsigned char uc[4];
} FUN;

void charRepresentation ( unsigned char *uc, float f)
{
    FUN fun;

    fun.f=f;
    uc[0]=fun.uc[3];
    uc[1]=fun.uc[2];
    uc[2]=fun.uc[1];
    uc[3]=fun.uc[0];
}

void floatRepresentation ( unsigned char *uc, float *f )
{
    FUN fun;
    fun.uc[3]=uc[0];
    fun.uc[2]=uc[1];
    fun.uc[1]=uc[2];
    fun.uc[0]=uc[3];
    *f=fun.f;
}

int main()
{
    unsigned int ra;
    float test;
    char result[4];
    FUN fun;

    if(sizeof(fun)!=4)
    {
        printf("It aint gonna work!\n");
        return(1);
    }

    test = 4.7567F;
    charRepresentation(result,test);
    for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");

    test = 1.0F;
    charRepresentation(result,test);
    for(ra=0;ra<;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");

    test = 2.0F;
    charRepresentation(result,test);
    for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");

    test = 3.0F;
    charRepresentation(result,test);
    for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");

    test = 0.0F;
    charRepresentation(result,test);
    for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");


    test = 0.15625F;
    charRepresentation(result,test);
    for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");

    result[0]=0x3E;
    result[1]=0xAA;
    result[2]=0xAA;
    result[3]=0xAB;
    floatRepresentation(result,&test);
    printf("%f\n",test);

    return 0;
}

И вывод выглядит так

gcc fun.c -o fun
./fun
0x40 0x98 0x36 0xE3
0x3F 0x80 0x00 0x00
0x40 0x00 0x00 0x00
0x40 0x40 0x00 0x00
0x00 0x00 0x00 0x00
0x3E 0x20 0x00 0x00
0.333333

Вы можете проверить вручную или посмотреть на этот сайт, так как я взял примеры непосредственно с него, выходные данные соответствуют ожидаемым.

http://en.wikipedia.org/wiki/Single_precision

То, что вы никогда не хотите делать, - это указывать на память указателем, чтобы посмотреть на нее другим типом. Я никогда не понимал, почему эта практика используется так часто, особенно со структурами.

int broken_code ( void )
{
    float test;
    unsigned char *result

    test = 4.567;
    result=(unsigned char *)&test;

    //do something with result here

    test = 1.2345;

    //do something with result here

    return 0;
}

Этот код будет работать 99% времени, но не 100% времени. Он потерпит неудачу, когда вы меньше всего этого ожидаете и в самый неподходящий момент, например, на следующий день после того, как ваш самый важный клиент получит его. Это оптимизатор, который ест ваш обед с этим стилем кодирования. Да, я знаю, что большинство из вас делают это, и этому учили, и, возможно, никогда не сжигали ... пока. Это только делает его более болезненным, когда это, наконец, происходит, потому что теперь вы знаете, что оно может и не удалось (с популярными компиляторами, такими как gcc, на обычных компьютерах, таких как ПК).

Увидев этот сбой при использовании этого метода для тестирования процессора, программно создавая конкретные числа / шаблоны с плавающей запятой, я переключился на объединенный подход, который до сих пор никогда не подводил. По определению, элементы в объединении совместно используют один и тот же кусок памяти, и компилятор и оптимизатор не путаются, когда два элемента в этом общем блоке хранения находятся ... в одном и том же общем блоке хранения. С помощью приведенного выше кода вы полагаетесь на предположение о том, что за каждым использованием переменных имеется незарегистрированная память, и что все переменные записываются обратно в это хранилище перед следующей строкой кода. Хорошо, если вы никогда не оптимизируете или используете отладчик. В этом случае оптимизатор не знает, что результат и тестирование совместно используют один и тот же кусок памяти, и это является причиной проблемы / ошибки. Чтобы сделать игру с указателями, вам нужно поставить все на переменные, например, объединение, вам все еще нужно знать, как компилятор выравнивает и дополняет, вы все равно должны иметь дело с порядком байтов.

Проблема в том, что компилятор не знает, что эти два элемента находятся в одном и том же пространстве памяти. В приведенном выше конкретном тривиальном примере я наблюдал, как компилятор оптимизировал присвоение числа переменной с плавающей запятой, потому что это значение / переменная никогда не используется. Используется адрес для хранения этой переменной, и если вы скажете printf * * data result, компилятор не оптимизирует указатель результата и, следовательно, не оптимизирует адрес для тестирования и, следовательно, не оптимизирует хранилище для тестирования, но в этом простом примере это может произойти, когда числа 4.567 и 1.2345 никогда не попадут в скомпилированную программу. Я также видел, как компилятор выделяет хранилище для теста, но назначает числа в регистр с плавающей запятой, затем никогда не использует этот регистр и не копирует содержимое этого регистра в хранилище, которое он назначил. Причины, по которым это не удается для менее тривиальных примеров, могут быть труднее понять, часто связанные с распределением и удалением регистров, изменением строки кода, и это работает, изменением другой и разрывом.

Memcpy,


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void charRepresentation ( unsigned char *uc, float *f)
{
    memcpy(uc,f,4);
}

void floatRepresentation ( unsigned char *uc, float *f )
{
    memcpy(f,uc,4);
}

int main()
{
    unsigned int ra;
    float test;
    unsigned char result[4];

    ra=0;
    if(sizeof(test)!=4) ra++;
    if(sizeof(result)!=4) ra++;
    if(ra)
    {
        printf("It aint gonna work\n");
        return(1);
    }

    test = 4.7567F;
    charRepresentation(result,&test);
    printf("0x%02X ",(unsigned char)result[3]);
    printf("0x%02X ",(unsigned char)result[2]);
    printf("0x%02X ",(unsigned char)result[1]);
    printf("0x%02X\n",(unsigned char)result[0]);

    test = 0.15625F;
    charRepresentation(result,&test);
    printf("0x%02X ",(unsigned char)result[3]);
    printf("0x%02X ",(unsigned char)result[2]);
    printf("0x%02X ",(unsigned char)result[1]);
    printf("0x%02X\n",(unsigned char)result[0]);

    result[3]=0x3E;
    result[2]=0xAA;
    result[1]=0xAA;
    result[0]=0xAB;
    floatRepresentation(result,&test);
    printf("%f\n",test);

    return 0;
}
gcc fcopy.c -o fcopy
./fcopy
0x40 0x98 0x36 0xE3
0x3E 0x20 0x00 0x00
0.333333

С пламенем я расскажу о своих комментариях выше, и в зависимости от того, на какой стороне аргумента вы решите быть. Возможно, memcpy - ваш самый безопасный маршрут. Вы все еще должны очень хорошо знать компилятор и управлять своими порядками байтов. Компилятор не должен испортить memcpy, он должен сохранить регистры в памяти перед вызовом и выполнить по порядку.

1 голос
/ 30 апреля 2010

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

0 голосов
/ 30 апреля 2010

A C char составляет всего 8 бит (на большинстве платформ). Основная проблема, которую это вызывает, двоякая. Во-первых, почти все существующие FPU поддерживают IEEE с плавающей запятой. Это означает, что значения с плавающей запятой либо требуют 32 бита, либо 64. Некоторые поддерживают другие нестандартные размеры, но я знаю только о 80 битах. Никто не слышал о поплавках из 8 бит. Таким образом, у вас не может быть аппаратной поддержки для 8-битного числа с плавающей запятой.

Что еще более важно, вы не сможете получить много цифр из 8-битного числа с плавающей запятой. Помните, что некоторые биты используются для представления показателя степени. У вас почти не осталось точности для ваших цифр.

Возможно, вместо этого вы хотите узнать о Фиксированная точка ? Это было бы выполнимо в байте.

0 голосов
/ 30 апреля 2010
int main()  
{  
    float test = 4.7567;  
    char result = charRepresentation(test);  
    return 0;  
}

Если мы игнорируем, что ваш float является float и конвертируем 47567 в двоичный код, мы получаем 10111001 11001111. Это 16 бит, что в два раза больше размера символа (8 бит). Числа с плавающей запятой хранят свои числа, сохраняя бит знака (+ или -), показатель степени (где ставить десятичную точку, в данном случае 10 ^ -1), а затем значащие цифры (47567). В полукоксе недостаточно места для хранения поплавка.

В качестве альтернативы, учтите, что символ может хранить только 256 различных значений. С точностью до четвертого знака после запятой существует намного больше 256 различных значений между 1 и 4.7567 или даже 4 и 4.7567. Поскольку вы не можете различить более 256 различных значений, у вас недостаточно места для его хранения.

Вы могли бы написать что-то, что могло бы «переводить» с плавающей запятой на символ, ограничивая себя чрезвычайно небольшим диапазоном значений и одним или двумя десятичными знаками *, но я не могу придумать причину, по которой вы захотите к.

* Вы можете хранить любое значение от 0 до 256 в символе, поэтому, если вы всегда умножаете значение в символе на 10 ^ -1 или 10 ^ -2 (вы можете использовать только один из этих параметров, но не оба, так как недостаточно места для хранения показателя) можно хранить любое число от 0 до 25,6 или от 0 до 2,56. Хотя я не знаю, к чему это приведет.

0 голосов
/ 30 апреля 2010

Для этого числа можно создать значение с фиксированной точкой, используя 2 бита для целого числа и 5 бит для дробной части (или 6, если вы хотите, чтобы оно было без знака). Это позволит хранить примерно 4,76 с точки зрения точности. У вас недостаточно размера для точного представления этого числа - если только вы не использовали таблицу поиска в ПЗУ из 256 записей, в которой вы храните информацию вне самого числа и в переводчике.

0 голосов
/ 30 апреля 2010

Вы можете сделать это только частично таким образом, чтобы вы не смогли полностью восстановить исходное значение. В общем, это называется Квантование , и в зависимости от ваших требований есть искусство выбора хорошего квантования. Например, значения с плавающей точкой, используемые для представления R, G и B в пикселе, будут преобразованы в символ перед отображением на экране.

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

...