9.2: «Так, я хотів би столик на 360, будь ласка»
- Page ID
- 29415
Розглянемо загальну функцію C trig, sin (). Не так багато до цього, насправді. Ви передаєте йому аргумент, і ви отримуєте назад синус аргументу. Але як це реалізується? Це може бути реалізовано як розширення серії Тейлора, яке вимагає декількох множень та додань. Один синус обчислення не займе багато часу, але що робити, якщо вам потрібно зробити мільйони з них? Все це примножує і додає, так би мовити, складаються. Розглянемо наступну проблему: Вам потрібно отримати синус кута, заданого до найближчого градуса, причому швидко. В основному, у вас є 360 можливих відповідей (0 градусів через 359 градусів) 1. Тепер припустимо, ви створите масив 360 значень, який складається з синуса кожного кута з кроком в один градус, починаючи з 0 градусів і працюючи до 359 градусів. Це може виглядати приблизно так:
double sine_table[] = { 0.0, 0.01745, 0.034899, 0.05234,
/* and so on to the last entry */ -0.01745 };
Тепер ви можете «обчислити» синус так, враховуючи кут аргументу:
answer = sine_table[ angle ];
Через подвійність масивів і покажчиків, ви також можете записати це як:
answer = *(sine_table + angle);
Без оптимізуючого компілятора друга форма, ймовірно, генерує більш ефективний машинний код. У будь-якому випадку це дуже швидко, оскільки це не що інше, як читання з місця пам'яті зі зміщенням. Результатом є лише одне додавання плюс доступ замість десятка множень/додає. Він приходить за ціною 360 подвійних поплавків, який, на вісім байт кожен, майже 3k пам'яті. Визнаючи, що синусоїдальна функція має симетрію чверті хвилі, ви можете додати трохи коду для перевірки квадранта та зменшити таблицю лише до 90 записів. Крім того, float s може мати достатню точність для програми, яка знову скоротить вимоги до пам'яті вдвічі порівняно з подвійними s.
Щоб зробити вищезазначене трохи більш швидким, ви завжди можете зробити його схожим на функцію за допомогою #define наступним чином:
#define fast_sin(a) (*(sine_table+(a)))
Звичайно, зворотною стороною цієї операції є те, що вона має справу лише з цілим аргументом і є точним лише до одного ступеня. Ви можете змінити визначення, щоб дозволити аргумент з плаваючою комою і округлити його до найближчого цілого ступеня наступним чином:
#define fast_sin(a) (*(sine_table+(int)((a)+0.5)))
Ви також можете перетворити це на справжню функцію та додати код для інтерполяції між сусідніми цілими записами ступеня для більш точного результату. У якийсь момент додаткові уточнення сповільнять функцію до того моменту, коли більш прямі обчислення стають конкурентоспроможними.
- За межами цих меж ви завжди можете виконати деяку незначну цілу математику, щоб потрапити в межі (наприклад, якщо кут 370, просто мод на 360, щоб отримати залишок 10 градусів, ефективно «обернути навколо»).
