Я решил разместить этот вопрос на Разработка игр так как мой случай относится к игровому проекту, но я подумал, что это больше подходит для более широкого программирования SE. Пожалуйста, дайте мне знать, если этот вопрос будет лучше опубликован там, в конце концов.
Исходя из моего понимания — и, пожалуйста, поправьте меня, если я ошибаюсь, — разработка игр для мобильных устройств (в моем случае) может значительно выиграть от использования вычислений с фиксированной запятой, поскольку это обеспечит большую согласованность между платформами и приведет к увеличению производительности в случаи, когда устройство недостаточно приспособлено для правильной работы с плавающей запятой.
Но у фиксированной точки есть ограничения, особенно с переполнением. Поэтому мой вопрос в том, что если мы определили, что фиксированная точка — лучшая альтернатива с учетом целевых платформ, как определить, позволяют ли требования данного проекта их использовать.
Чтобы было понятнее, я хотел бы поделиться небольшим количеством кода, с которым у меня возникли проблемы:
CIwVec2 calculateBezier(float t, CIwVec2 p0, CIwVec2 p1, CIwVec2 p2) {
float u = 1 - t;
CIwVec2 p = IW_FIXED(u * u) * p0; // (1 - t)²P0
p += IW_FIXED(2 * u * t) * p1; // 2(1 - t)tP1
p += IW_FIXED(t * t) * p2; // t²P2
return p;
}
В этом проекте я использую Мармелад SDK, который использует C ++ и поставляется с собственной реализацией чисел с фиксированной запятой (у них есть как 16, так и 32-разрядные, я использую 32-разрядные на данный момент), и класс Vector (CIwVec2), который использует эту реализацию для позиции и вычисления (в том числе скалярное умножение, которое показано в коде выше). Да, и IW_FIXED — это просто макрос для преобразования чисел с плавающей запятой в фиксированную.
Когда я пытаюсь запустить приведенный выше код, я получаю Умножение переполнения ошибка. Отладка значений следующим образом:
t = 0
u = 1 (which converts to 4096 in int32 fixed-point with IW_FIXED)
p0.x = 1638400 (400.0f with IW_FIXED_TO_FLOAT)
p0.y = 409600 (100.0f with IW_FIXED_TO_FLOAT)
Честно говоря, у меня нет полного понимания чисел с фиксированной точкой. Я понимаю идею, но операции с фиксированной запятой мне не совсем понятны (я, должно быть, отбросил большинство математических классов, связанных с основанием 2, позор мне). Но я совершенно поражен тем фактом, что что-то простое, например, 1.0f * 400.0f, может вызвать переполнение с фиксированной запятой.
Поэтому, хотя я думал, что у меня не будет проблем с поддержкой фиксированной точки в моем проекте, похоже, это может быть не так. Игра представляет собой автомобильную игру «сверху вниз», в которой не будет огромных дорожек или чего-то еще, но они должны быть как минимум такими же большими, как экран устройства (или, еще лучше, его разрешение), и поскольку мы нацеливаемся для планшетов проблемы с чем-то вроде 1.0f * 400.0f означают, что о фиксированной точке не может быть и речи.
Я прав с этим предположением? И для будущих проектов и для других людей с похожими проблемами, как мы можем оценить жизнеспособность номеров с фиксированной точкой в проекте? Кроме того, как выбрать между 16-битным и 32-битным было бы отличным бонусом 🙂
(Извините за длинный пост и спасибо за ваше время!)
Обновить:
Итак, подведем итоги. Идеальный сценарий — реализовать числа с фиксированной точкой таким образом, чтобы иметь необходимый диапазон для ваших нужд (ответ Mooing Duck). Кроме того, для операций с 32-разрядными числами наиболее безопасным является вычисление с использованием 64-разрядных (ответ Timday и комментарий Макса). Кстати, у Marmalade есть некоторые функции «безопасного фиксированного умножения», но это не так с перегрузкой оператора скалярного умножения для CIwVec2 (которая использует IW_FIXED_MUL
внизу, что не безопасно для mupliply).
И, наконец, больше в отношении моего конкретного сценария. Похоже, что начиная с Marmalade 6.1, просто использование поплавков было бы лучшим решением.
Редактировать:
Хотя ответ Макса действительно решил мою проблему, это было главным образом потому, что это было что-то особенное для Мармелада. Из-за этого я выбрал ответ Mooing Duck в качестве выбранного ответа, так как считаю, что он поможет большему количеству людей.
Фиксированная точка может рассматриваться как хранящая верхнюю половину дроби, а нижняя половина в вашем случае — 4096. Таким образом, 1.0f равен 4096/4096, и он хранится в памяти как 4096. Я не буду вдаваться в детали дробного умножения, потому что я не помню их, но важная часть при умножении A/D * B/D
чтобы получить результат C/D
, затем C
является A*B/D
,
Итак, где у вас есть 1.0f * 400.0f
компьютер видит это как 4096 * 1638400 / 4096
, Небольшая алгебра показывает, что это должно привести к 1638400 (400.0f), но если библиотека дробных точек не достаточно умна для этого, она завершается временным значением 6710886400, прежде чем выполнить деление, которое слишком велико для int
,
Поскольку ваши плавающие точки имеют знаменатель 4096, ваши «плавающие» с точностью до ближайших 0,00024 имеют диапазон от -524288,9999f до 524287,9999f (иш). Есть ли способ в вашем компиляторе снизить точность «поплавков», чтобы получить больший диапазон? В противном случае вы будете на месте.
Макс подтверждает, что 4096 является частью мармелада и не может быть изменен. В этом случае я вижу несколько вариантов:
(1) Не используйте фиксированную точку.
(2) Старайтесь, чтобы все числа с плавающей запятой находились в диапазоне от -128 до 128.
(3) Используйте специальную функцию для умножения скаляра и CIwVec2
который использует IW_FIXED_MUL
под. Желательно обернуть float
в специальном классе и перегрузить operator*
звонить IW_FIXED_MUL
, Не предоставляйте неявное преобразование в float
иначе вы никогда не найдете все ошибки.
То, что вы видите, и 32-битные промежуточные соединения, которые оно подразумевает (см. Ответ Mooing Duck), кажется мне серьезной слабостью.
В прошлом я успешно использовал библиотеки с фиксированной точкой (на x86 в эпоху 386/486), и все они использовали 64-битное промежуточное звено для умножения перед корректирующим сдвигом (фактически я помню, что Intel предоставила очень хорошие 32×32-to-64 инструкция умножения битов, которая была просто идеальной для этого). Более высокая промежуточная точность принесла вам большую свободу в диапазонах данных; Мне бы очень не хотелось работать с чем-то таким ограниченным, каким кажется эта библиотека Marmalade, но я понимаю, почему они могли сделать это по причинам переносимости.
Сегодня нет смысла использовать фиксированную точку в мобильной разработке при использовании Marmalade 6.1.
Мармелад 6.1 вышел в сентябре.
Есть немного телефонов Android без FPU, но большинство телефонов имеет FPU, GPU с GLES 1.0 и GLES 2.0
Теперь мы нацелены на устройства GLES 2.0 с мармеладом.
Причина, по которой у Marmalade есть IW_FIXED, заключается в том, что у них есть собственный графический слой под названием IwGX.
До сентября 2012 г. (мармелад 6.1). IwGX не поддерживает плавающую точку.
Вот почему разработчики были вынуждены использовать фиксированные номера, даже когда они предназначались для последних устройств GLES 2.0.
Сегодня нет смысла использовать фиксированные номера с мармеладом.
—
Во всяком случае, если вы все еще хотите множить с фиксированной точкой, есть IW_FIXED_MUL функция
ДОБАВЛЕНО: Код в примере правильный.