2016-11-28 2 views
1

Справочная информация:
Я играю с кодированием бит уровня (это не домашнее задание - просто интересно). Я нашел много хорошего материала в Интернете и в книге под названием Hacker's Delight, но у меня проблемы с одной из проблем онлайн.C Bit-Level Int к Float Conversion непредвиденного вывода

Он запрашивает преобразование целого числа в поплавок. Я использовал следующие ссылки в качестве ссылки на работу по проблеме:

How to manually (bitwise) perform (float)x?
How to convert an unsigned int to a float?
http://locklessinc.com/articles/i2f/

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

Test Cases:
float_i2f (2) возвращает 1073741824
float_i2f (3) возвращает 1077936128

Я ожидал увидеть что-то вроде 2.0000 и 3.0000.

Я где-то испортил конверсию? Я подумал, может быть, это адрес памяти, поэтому я подумал, может быть, я пропустил что-то на этапе преобразования, необходимого для доступа к фактическому числу? Или, может быть, я печатаю его неправильно? Я печать мой такой вывод:

printf("Float_i2f (%d): ", 3); 
printf("%u", float_i2f(3)); 
printf("\n"); 

Но я думал, что метод печати было хорошо для беззнаковых значений в C (я привык к программированию на Java).

Спасибо за любой совет.

Код:

/* 
    * float_i2f - Return bit-level equivalent of expression (float) x 
    * Result is returned as unsigned int, but 
    * it is to be interpreted as the bit-level representation of a 
    * single-precision floating point values. 
    * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while 
    * Max ops: 30 
    * Rating: 4 
    */ 
    unsigned float_i2f(int x) { 
     if (x == 0){ 
      return 0; 
     } 

     //save the sign bit for later and get the asolute value of x 
     //the absolute value is needed to shift bits to put them 
     //into the appropriate position for the float 
     unsigned int signBit = 0; 
     unsigned int absVal = (unsigned int)x; 

     if (x < 0){ 
      signBit = 0x80000000; 
      absVal = (unsigned int)-x; 
     } 

     //Calculate the exponent 
     // Shift the input left until the high order bit is set to form the mantissa. 
     // Form the floating exponent by subtracting the number of shifts from 158. 
     unsigned int exponent = 158; //158 possibly because of place in byte range 

     while ((absVal & 0x80000000) == 0){//this checks for 0 or 1. when it reaches 1, the loop breaks 
      exponent--; 
      absVal <<= 1; 
     } 

     //find the mantissa (bit shift to the right) 
     unsigned int mantissa = absVal >> 8; 

     //place the exponent bits in the right place 
     exponent = exponent << 23; 

     //get the mantissa 
     mantissa = mantissa & 0x7fffff; 

     //return the reconstructed float 
     return signBit | exponent | mantissa; 
    } 
+2

Вы должны использовать 'формат% f' спецификатор сказать' printf' интерпретировать значение как значение с плавающей точкой. Используя '% u', вы попросили его распечатать целое число без знака. Однако это может быть неопределенное поведение из-за строгого сглаживания, а также способ передачи переменных аргументов функции. Возможно, вам лучше создать переменную 'float' и использовать' memcpy', чтобы скопировать результирующие целочисленные биты непосредственно в float. Конечность все еще будет проблемой. Как глубоко ты хочешь пойти? – paddy

+2

Это выглядит правильно (я не пошел, хотя ваши расчеты). То, что вы ищете, - это целое число без знака * эквивалент * битов, которые составляют число с плавающей точкой с одиночной точностью IEEE-754. Вы можете создать простой 'union'' float' и 'uint32_t' и проверить вывод обоих для подтверждения. –

+1

Ваш код в порядке, хотя он не круглый, только усекает. – deamentiaemundi

ответ

3

Продолжая комментарий. Ваш код верен, и вы просто смотрите на эквивалент unsigned integer, составленный битами в вашем номере с плавающей точкой с одиночной точностью IEEE-754. Формат одиночной точности IEEE-754 (состоящий из знака, расширенной экспоненты и мантиссы) можно интерпретировать как float, или эти же биты можно интерпретировать как unsigned integer (только число, которое составлено 32 бита). Вы получаете беззнаковый эквивалент для номера с плавающей запятой.

Вы можете подтвердить простым соединением. Например:

#include <stdio.h> 
#include <stdint.h> 

typedef union { 
    uint32_t u; 
    float f; 
} u2f; 

int main (void) { 

    u2f tmp = { .f = 2.0 }; 
    printf ("\n u : %u\n f : %f\n", tmp.u, tmp.f); 

    return 0; 
} 

Пример/Output

$ ./bin/unionuf 

u : 1073741824 
f : 2.000000 

Позвольте мне знать, если у вас есть какие-либо дополнительные вопросы. Хорошо видеть, что ваше исследование привело к правильному преобразованию с плавающей точкой.(также обратите внимание на второй комментарий об усечении/округлении)

+0

Спасибо за связку. Мне нужно немного почитать, но я думаю, что понял. Из-за отсутствия лучшего способа выразить это, поплавки и неподписанные ints рассказывают две разные истории.Таким образом, у них есть две разные спецификации, которые описывают их. Это также означает, что представление каждого уровня бит будет отличаться. Поэтому, когда вы распечатываете их, мы говорим, какую спецификацию использовать. Я говорил программе использовать неподписанную версию. Кроме того, я обязательно вернусь к закруглению. Это заняло некоторое время, чтобы понять, поэтому я пытаюсь сделать все за один шаг. – JustBlossom

+0

Да, у вас оно есть. 32-бит - всего 32 бита. Если вы посмотрите на них через окно «float» (например, как * sign-bit *, за которым следуют 8-бит * экспонента * и 23-бит * mantissa *), вы увидите, что они представляют как float, когда вы просматриваете окно «unsigned» (учитывая биты 0-31 в целом), вы получаете то, что будет означать «unsigned» для этих битов. В любом случае, это одни и те же биты. Это то, как float представляют собой стихи, которые мы называем целыми числами. –

0

Я просто позвоню здесь, потому что ничего конкретно о контенте не было адресовано. Поэтому давайте поговорим об этом.

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

  2. Общепринятое соглашение для IEEE-754 состоит в том, что порядок байтов является широкоформатным (хотя я считаю, что формальной спецификации этого нет, и поэтому нет необходимости в реализации для его реализации). Это означает, что если вы хотите прямо интерпретировать целочисленное значение как float, его нужно выложить в порядке байтов байтов.

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

На общих архитектурах на базе Intel это не нормально. На этих архитектурах целые числа являются малорисковыми, а float - big-endian. Вам нужно преобразовать свое значение в big-endian. Простой подход к этому является упакуйте его байт , даже если они уже большой обратный порядок байт:

uint32_t n = float_i2f(input_val); 
uint8_t char bytes[4] = { 
    (uint8_t)((n >> 24) & 0xff), 
    (uint8_t)((n >> 16) & 0xff), 
    (uint8_t)((n >> 8) & 0xff), 
    (uint8_t)(n & 0xff) 
}; 
float fval; 
memcpy(&fval, bytes, sizeof(float)); 

Я подчеркиваю, что вам только нужно беспокоиться об этом, если вы пытаетесь переосмысливать ваше целое представление в виде a float или наоборот.

Если вы пытаетесь вывести то, что представляет собой бит, вам не нужно беспокоиться. Вы можете просто отображать число в удобной форме, такие как гекс:

printf("0x%08x\n", n); 
+0

x86 использует little-endian для float & double values, как и для всех значений ... –

+0

Моя ошибка. Возможно, меня смутило порядок сетевых байтов. – paddy

+0

сетевое байтовое огибание является бигендианским - это означает, что если вы хотите передавать поплавки/удваивания в сетевом порядке на x86, вам нужно будет использовать байты. –