Правила явного int32 -> приведения типа float32 - PullRequest
2 голосов
/ 15 февраля 2012

У меня есть домашнее задание для эмуляции приведений с плавающей запятой, например ::100100

int y = /* ... */;
float x = (float)(y);

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

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

Конечно, я получаю расплывчатые вещи, такие как «округление до нуля» с разбросанных сайтов, но, честно говоря, мои поиски продолжают получать забитые C вопросы новичка (например, «Что такое бросок?», «Когда я его использую?»). Итак, я не могу найти общее правило, которое работает для явного определения показателя и мантиссы.

Помощь? Спасибо,
Ian

Ответы [ 3 ]

2 голосов
/ 15 февраля 2012

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

Я предполагаю, что ваше представление с плавающей запятой соответствует стандарту IEEE 754, и что округление выполняется так же, как это делают MSVC и MinGW: используется схема «банковского округления» (я, честно говоря, не уверен, требуется ли эта конкретная схема округления) по стандарту, это то, что я проверял, хотя). В оставшейся части обсуждения предполагается, что значение int должно быть преобразовано в значение, большее 0. Отрицательные числа могут быть обработаны, имея дело с их абсолютным значением и устанавливая бит знака в конце. Конечно, 0 необходимо обрабатывать специально в любом случае (потому что нет msb для поиска).

Поскольку в мантиссе есть 24 бита точности (включая подразумеваемое 1 для msb), можно точно представить значения до 16777215 (или 0x00ffffff). Нет ничего особенного, кроме сдвига битов, чтобы привести вещи в нужное место и вычислить правильный показатель степени в зависимости от сдвигов.

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

  • Если значение отброшенного бита равно 0, больше ничего делать не нужно. Мантиссу и экспоненту можно оставить в покое.
  • если msb отброшенных битов равно 1, а оставшимся отброшенным битам задан один или несколько битов, мантисса должна быть увеличена. Если мантисса переполняется (за 24 бита, если вы еще не отбросили подразумеваемое значение msb), тогда мантисса должна быть сдвинута вправо, а показатель степени увеличен.
  • , если msb отброшенных битов равен единице, а все оставшиеся пропущенные биты равны 0, тогда мантисса увеличивается только , если lsb равно 1. Обработка переполнения мантиссы аналогично случаю 2.

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

1 голос
/ 15 февраля 2012

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

Что касается моего кода, я покопался в нем и нашел его, но должен предупредить вас, что он уродлив, и поскольку он был для личного проекта (мой тезис старшекурсника), он не прокомментирован должным образом. Также у кода могут быть определенные особенности, поскольку он предназначен для встроенной системы (робота). Ссылка на страницу, которая объясняет проект и содержит ссылку для загрузки кода: здесь . Я не против веб-сайта, я не веб-дизайнер. Боюсь:)

Вот как я представлял числа с плавающей точкой в ​​этом проекте:

typedef struct
{
    union{
        struct {
           unsigned long mantissa: 23;
           unsigned long exponent: 8;
           unsigned long sign: 1;
       } float_parts;   //the struct shares same memory space as the float
                        //allowing us to access its parts with the bitfields

        float all;

    };

}_float __attribute__((__packed__));

Используются битовые поля , объяснение которых, я думаю, выходит за рамки темы, поэтому обратитесь к ссылке, если вы хотите узнать больше информации.

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

_float intToFloat(int number)
{
    int i;
    //will hold the resulting float
    _float result;

    //depending on the number's sign determine the floating number's sign
    if(number > 0)
        result.float_parts.sign = 0;
    else if(number < 0)
    {
        number *= -1; //since it would have been in twos complements
                     //being negative and all
        result.float_parts.sign = 1;
    }
    else // 0 is kind of a special case
    {
        parseFloat(0.0,&result);
        return result;
    }

    //get the individual bytes (not considering endiannes here, since it is for the robot only for now)
    unsigned char* bytes= (unsigned char*)&number;

    //we have to get the most significant bit of the int
    for(i = 31; i >=0; i --)
    {
        if(bytes[i/8] & (0x01 << (i-((i/8)*8))))
            break;
    }


    //and adding the bias, input it into the exponent of the float
    //because the exponent says where the decimal (or binary) point is placed relative to the beginning of the mantissa
    result.float_parts.exponent = i+127;


    //now let's prepare for mantissa calculation
    result.float_parts.mantissa = (bytes[2] <<  16 | bytes[1] << 8 | bytes[0]);

    //actual calculation of the mantissa
    i= 0;
    while(!(result.float_parts.mantissa & (0x01<<22)) && i<23) //the i is to make sure that
    {                                                          //for all zero mantissas we don't
        result.float_parts.mantissa <<=1;                      //get infinite loop
        i++;
    }
    result.float_parts.mantissa <<=1;


    //finally we got the number
    return result;
}
0 голосов
/ 16 февраля 2012

Спасибо всем за очень полезную помощь!В частности, правила округления были особенно полезны!

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

unsigned float_i2f(int x) {
    /* Apply a complex series of operations to make the cast.  Rounding was achieved with the help of my post /7887086/pravila-yavnogo-int32-privedeniya-tipa-float32. */
    int sign, exponent, y;
    int shift, shift_is_pos, shifted_x, deshifted_x, dropped;
    int mantissa;

    if (x==0) return 0;

    sign = x<0 ? 0x80000000 : 0; //extract sign
    x = sign ? -x : x; //absolute value, sorta

    //Check how big the exponent needs to be to offset the necessary shift to the mantissa.
    exponent = 0;
    y = x;
    while (y/=2) {
        ++exponent;
    }

    shift = exponent - 23; shift_is_pos = shift >= 0; //How much to shift x to get the mantissa, and whether that shift is left or right.

    shifted_x = (shift_is_pos ? (x>>shift) : (x<<-shift)); //Shift x
    deshifted_x = (shift_is_pos ? (shifted_x<<shift) : (shifted_x>>-shift)); //Unshift it (fills right with zeros)
    dropped = x - deshifted_x; //Subtract the difference.  This gives the rounding error.

    mantissa = 0x007FFFFF & shifted_x; //Remove leading MSB (it is represented implicitly)

    //It is only possible for bits to have been dropped if the shift was positive (right).
    if (shift_is_pos) {
        //We dropped some bits.  Rounding may be necessary.
        if ((0x01<<(shift-1))&dropped ) {
            //The MSB of the dropped bits is 1.  Rounding may be necessary.

            //Kill the MSB of the dropped bits (taking into account hardware ignoring 32 bit shifts).
            if (shift==1) dropped = 0;
            else dropped <<= 33-shift;

            if (dropped) {
                //The remaining dropped bits have one or more bits set.
                goto INC_MANTISSA;
            }
            //The remaining dropped bits are all 0
            else if (mantissa&0x01) {
                //LSB is 1
                goto INC_MANTISSA;
            }
        }
    }

    //No rounding is necessary
    goto CONTINUE;

    //For incrementing the mantissa.  Handles overflow by incrementing the exponent and setting the mantissa to 0.
INC_MANTISSA:
    ++mantissa;
    if (mantissa&(0x00800000)) {
        mantissa = 0;
        ++exponent;
    }

    //Resuming normal program flow.
CONTINUE:
    exponent += 127; //Bias the exponent

    return sign | (exponent<<23) | mantissa; //Or it all together and return.
}

Она правильно решает все тестовые случаи, хотя я уверен, что она не обрабатывает все правильно (например, если x равен 0x80000000, то "раздел "абсолютное значение" вернет 0x80000000 из-за переполнения).

Еще раз хочу поблагодарить всех вас за помощь!

Спасибо, Ян

...