Подтвердить что ты не робот

Получение дробной части поплавка без использования modf()

Я разрабатываю платформу без математической библиотеки, поэтому мне нужно создать собственные инструменты. Мой текущий способ получения фракции - преобразовать float в фиксированную точку (умножить на (float) 0xFFFF, cast to int), получить только нижнюю часть (маску с 0xFFFF) и снова преобразовать ее обратно в float.

Однако неточность убивает меня. Я использую функции Frac() и InvFrac() для создания сглаженной линии. Используя modf, я получаю совершенно гладкую линию. С моим собственным методом пиксели начинают прыгать из-за потери точности.

Это мой код:

const float fp_amount = (float)(0xFFFF);
const float fp_amount_inv = 1.f / fp_amount;

inline float Frac(float a_X)
{
    return ((int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv;
}

inline float Frac(float a_X)
{
    return (0xFFFF - (int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv;
}

Спасибо заранее!

4b9b3361

Ответ 1

Если я правильно понял ваш вопрос, вам просто нужна часть после десятичного правого? Вам это не нужно на самом деле во фракции (целочисленный числитель и знаменатель)?

Итак, у нас есть некоторое число, скажем 3.14159, и мы хотим получить только 0.14159. Предполагая, что наш номер хранится в float f;, мы можем сделать это:

f = f-(long)f;

Что, если мы вставляем наш номер, работает следующим образом:

0.14159 = 3.14159 - 3;

То, что это делает, - это удаление целой числовой части поплавка, оставляющей только десятичную часть. Когда вы конвертируете float в длинный, он отбрасывает десятичную часть. Затем, когда вы вычитаете это из вашего исходного поплавка, вы остаетесь только с десятичной частью. Нам нужно использовать длинный номер из-за размера типа float (8 байтов в большинстве систем). Целое число (всего 4 байта на многих системах) не обязательно достаточно велико, чтобы охватить тот же диапазон чисел, что и float, но a long должен быть.

Ответ 2

Как я и предполагал, modf не использует арифметику как таковую - все сдвиги и маски, посмотрите здесь. Не можете ли вы использовать те же идеи на своей платформе?

Ответ 3

Я бы порекомендовал взглянуть на то, как modf реализован в системах, которые вы используете сегодня. Проверьте версию uClibc.

http://git.uclibc.org/uClibc/tree/libm/s_modf.c

(По юридическим причинам он, по-видимому, лицензирован BSD, но вы, очевидно, хотите дважды проверить)

Некоторые макросы определены здесь.

Ответ 4

Там есть ошибка в ваших константах. Вы в основном пытаетесь сделать левый сдвиг числа на 16 бит, замаскируйте все, кроме младших бит, а затем снова сдвиньте на 16 бит снова. Сдвиг - это то же самое, что умножение на мощность 2, но вы не используете мощность 2 - вы используете 0xFFFF, который выключен на 1. Замена этого 0x10000 заставит формулу работать по назначению.

Ответ 5

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

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

Для описания механизма хранения 32-битных поплавков посмотрите здесь.

Ответ 6

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

Рисунок линии Bresenham

Ответ 7

Похоже, что вы этого хотите.

float f = something;
float fractionalPart = f - floor(f);

Ответ 8

Ваш метод предполагает, что в дробной части есть 16 бит (и как отмечает Mark Ransom, это означает, что вы должны сдвинуть на 16 бит, то есть умножить на 0x1000). Это может быть неверно. Показатель - это то, что определяет, сколько бит есть в дробной части.

Чтобы поместить это в формулу, ваш метод работает, вычисляя (x modf 1.0) как ((x << 16) mod 1<<16) >> 16, и это жестко закодированное 16, которое должно зависеть от экспоненты - точная замена зависит от вашего формата float.

Ответ 9

Один из вариантов - использовать fmod(x, 1).