Фиксированная точка обратного синуса - PullRequest
2 голосов
/ 15 ноября 2009

Кто-нибудь знает (желательно быстрый) способ вычисления синуса угла в фиксированной точке 4,12? (где результат равен 32768 долям круга или градусам)

4.12 с фиксированной точкой означает, что число составляет 16 бит и смещено влево на 12, поэтому 1,0 становится (1 << 12) или 4096. 0,5 - это (0,5 << 12) == 2048 и т. Д.

Ответы [ 2 ]

8 голосов
/ 15 ноября 2009

Предварительный расчет - это путь, если вы хотите максимальную скорость. Поскольку у вас есть 2 16 (65 536) возможных входов, наивный подход заключается в том, чтобы иметь массив из такого количества 16-битных значений (всего используется 128 КБ памяти).

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

Предполагая, что ваши входные данные выражены в радианах (я предполагаю, что они таковы, поскольку ваши диапазоны для числа 4.12 составляют от 0 до 15.999 ..., недостаточно для представления всех градусов по кругу), вы можете воспользоваться фактами, которые :

  • sin (n) = sin (n % (2*PI)) for n >= 2*PI
  • sin (n) = -sin (n - PI) for PI <= n < 2*PI
  • sin (n) = sin (PI - n) for PI/2 <= n < PI

Таким образом, ваша справочная таблица должна содержать только значения от 0 до PI / 2 радиан, что резко сокращает требования к хранилищу: вы храните только от 0 до PI / 2 (~ 1.571), а не полный диапазон от 0 до 15.999 .. ., сокращение примерно на 90%.

Тогда вам просто нужно уменьшить значения до значений ниже 2 * Пи радиан по модулю (первое правило) и изменить его с помощью двух других правил, чтобы найти правильный индекс для поиска. Модуль будет работать с фиксированной точкой так же быстро, как и с целыми числами.

Так что вы бы смотрели на что-то вроде:

def sin(r):
    if r >= PI_BY_2:
        return sin (r % PI_BY_2)
    if r >= PI:
        return -sin (r - PI)
    if r >= PI_DIV_2:
        return sin (PI - r)
    return sin_lookup[r]
def cos(r):
    if r < PI_DIV_2:
        return sin (r + PI_DIV_2_BY_3)
    return sin (r - PI_DIV_2)

Эта функция cos показывает, как дешево получать косинусы из одной таблицы, поскольку на самом деле они имеют сдвиг фазы на 90 градусов по отношению к синусам.

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

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

Учитывая, что функция синуса является гладкой (без разрывов), вы можете хранить каждое второе значение и интерполировать значения для промежуточных значений, просто усредняя значения по обе стороны от нее.

Например, если у нас была функция f(x) = x * x, следует таблица действительных и интерполированных значений (предположим, что мы сохраняем значения только для четных x, нечетные значения x интерполируются, отмечены * ниже):

 x    real f(x)   interpolated f(x)
--    ---------   -----------------
 0            0                   0
 1            1                   2 *
 2            4                   4
 3            9                  10 *
 4           16                  16
 5           25                  26 *
 6           36                  36
 7           49                  50 *
 8           64                  64
 9           91                  82 *
10          100                 100

Теперь это не идеальный пример, поскольку разброс между значениями f (x) может быть довольно большим, но он лучше работает для функций, где значения ближе друг к другу.

Например, действительные значения sin (0), sin (1) и sin (2) (градусы, а не радианы) равны 0, 0,017452406 и 0,034899496 (и именно здесь наклон является наибольшим). Среднее значение sin (0) и sin (2) составляет 0,017449748, что составляет погрешность 0,0152% от действительного значения, одна часть в 6500

.

Таким образом, для минимальной ошибки вы могли бы вдвое сократить требования к памяти примерно до 5% от 128 КБ или до шести с половиной К.

1 голос
/ 15 ноября 2009

Самый быстрый способ - просто предварительно рассчитать их и сохранить в справочной таблице.

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

...