C ++: эмулированное деление с фиксированной запятой / умножение - PullRequest
1 голос
/ 17 февраля 2011

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

class Fixed
{
    Fixed(short int _value, short int _part) : 
        value(long(_value + (_part >> 8))), part(long(_part & 0x0000FFFF)) {};

    ...

    inline Fixed operator -() const  // example of some of the bitwise it's doing
    {
        return Fixed(-value - 1, (~part)&0x0000FFFF);
    };

    ...

    inline Fixed operator / (const Fixed & arg) const // example of how I'm probably doing it wrong
    {
        long int tempInt = value<<8 | part;
        long int tempPart = tempInt;
        tempInt  /= arg.value<<8 | arg.part;
        tempPart %= arg.value<<8 | arg.part;
        return Fixed(tempInt, tempPart);
    };

    long int value, part; // members
};

Я ... я не очень хороший программист, хаха!

'Часть' класса имеет ширину 16 бит (но она выражается как длинный 32 бит, так как я предполагаю, что потребуется место для возможных переполнений, прежде чем они будут исправлены), и то же самое относится и к 'значению', которое является целочисленной частью , Когда «часть» переходит 0xFFFF в одной из своих операций, старшие 16 бит добавляются к «значению», а затем часть маскируется, так что остаются только самые младшие 16 бит. Это сделано в списке инициализации.

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

Ответы [ 3 ]

2 голосов
/ 17 февраля 2011

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

«Хитрость» заключается в том, чтобы понять, что происходит с «форматом» числа при выполнении операций с ним. Ваш формат будет описан как 16.16. Когда вы добавляете или вычитаете, формат остается прежним. Когда вы умножаете, вы получаете 32,32 - поэтому вам нужно 64-битное временное значение для результата. Затем вы делаете сдвиг >> 16, чтобы перейти к формату 48.16, затем берете 32 младших бита, чтобы получить ответ в 16.16.

Я немного заржавел в подразделении - в DSP, где я изучил этот материал, мы избегали (дорогого) разделения везде, где это возможно!

0 голосов
/ 17 февраля 2011

Чтобы начать работу, сначала внедрите (унарный) inverse(x) = 1/x, а затем a/b как a*inverse(b). Возможно, вы захотите представить промежуточные продукты в формате 32.32.

0 голосов
/ 17 февраля 2011

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

  • Умножение:

    operator*(const Fixed &other) const {
        return Fixed((int64_t)value * (int64_t)other.value);
    }
    
  • Отдел:

    operator/(const Fixed &other) const {
        return Fixed(((int64_t)value << 16) / (int64_t)other.value);
    }
    

64-разрядные целые числа

  • На gcc должна быть доступна stdint.h (или cstdint, которая помещает их в пространство имен std::), чтобы вы могли использовать типы, которые я упоминал выше. В противном случае это long long для 32-битных целей и long для 64-битных целей.
  • В Windows это всегда long long или __int64.
...