Просто для удовольствия и чтобы доказать, что это возможно, я закончил процедуру сборки AVR для вычисления результатов sin (x) в 24 битах (3 байта) с одним битом ошибки. Угол ввода указывается в градусах с одной десятичной цифрой, от 000 до 900 (0 ~ 90,0) только для первого квадранта. Он использует менее 210 инструкций AVR и работает в среднем 212 микросекунд, варьируя от 211us (угол = 001) до 213us (угол = 899).
Все это заняло несколько дней, более 10 дней (свободные часы), чтобы подумать о лучшем алгоритме расчета, с учетом микроконтроллера AVR, без плавающей запятой, исключив все возможные деления. Что потребовало больше времени, чтобы сделать правильные повышающие значения для целых чисел, чтобы иметь хорошую точность, необходимо повысить значения от 1e-8 до двоичных целых чисел 2 ^ 28 или больше. Как только все виновники ошибок точности и округления были найдены, их разрешение вычислений увеличилось на дополнительные 2 ^ 8 или 2 ^ 16, наилучшие результаты были достигнуты. Сначала я смоделировал все вычисления в Excel, следя за тем, чтобы все значения, такие как Int (x) или Round (x, 0), точно представляли обработку ядра AVR.
Например, в алгоритме угол должен быть в радианах, а ввод в градусах для удобства пользователя. Чтобы преобразовать градусы в радианы, используется тривиальная формула: рад = градусы * PI / 180, это выглядит красиво и легко, но это не так, PI - это бесконечное число - если использование нескольких цифр приведет к ошибкам на выходе, для деления на 180 Манипулирование битами AVR, так как оно не имеет инструкции деления, и более того, в результате потребуется число с плавающей запятой, так как в нем используются числа, намного меньшие целого числа 1. Например, радиан 1 ° (градуса) равен 0,017453293. Поскольку PI и 180 являются константами, почему бы не изменить это на простое умножение? PI / 180 = 0,017453293, умножьте его на 2 ^ 32, и получится константа 74961320 (0x0477D1A8), умножьте это число на свой угол в градусах, скажем, 900 для 90 ° и сдвинем его на 4 бита вправо (÷ 16), чтобы получить 4216574250 (0xFB53D12A), то есть радианы 90 ° с расширением 2 ^ 28, помещаются в 4 байта, без единого деления (кроме 4 сдвиг вправо). В некотором смысле, ошибка, включенная в такой трюк, меньше, чем 2 ^ -27.
Таким образом, все дальнейшие вычисления должны помнить, что это на 2 ^ 28 выше и заботиться об этом. Вам нужно разделить результаты на ходу на 16, 256 или даже 65536, чтобы избежать использования ненужных растущих байтов голода, которые не помогут при разрешении. Это была кропотливая работа, просто найти минимальное количество бит в каждом результате вычислений, поддерживая точность результатов около 24 бит. Каждое из нескольких вычислений было выполнено в попытке / ошибке с большим или меньшим числом битов в потоке Excel, отслеживая общее количество битов ошибок в результате на графике, показывающем 0-90 ° с макросом, выполняющим код 900 раз, один раз за десятую часть степени. Этот «визуальный» подход Excel был инструментом, который я создал, очень помог найти лучшее решение для каждой части кода.
Например, округляя этот конкретный результат вычисления с 13248737.51 до 13248738 или просто теряя десятичные дроби "0.51", насколько это повлияет на точность конечного результата для всех 900 тестов входных углов (00.1 ~ 90.0)?
Я мог держать животное в пределах 32 бит (4 байта) при каждом расчете, и в результате получил магию для получения точности в пределах 23 бит от результата. При проверке целых 3 байтов результата ошибка составляет ± 1 младший бит, выдающийся.
Пользователь может получить один, два или три байта из результата для своих требований точности. Конечно, если достаточно одного байта, я бы рекомендовал использовать одну таблицу sin размером 256 байт и использовать инструкцию AVR 'LPM' для ее получения.
После того как последовательность Excel стала гладкой и аккуратной, окончательный перевод из Excel в сборку AVR занял менее 2 часов, как обычно вы должны думать в первую очередь, работать позже.
В то время я смог еще больше сжать и уменьшить использование регистров. Фактический (не окончательный) код использует около 205 инструкций (~ 410 байт), выполняет вычисление sin (x) в среднем 212us, тактовая частота 16 МГц. На этой скорости он может вычислять 4700+ sin (x) в секунду. Не важно, но он может работать с точной синусоидой до 4700 Гц с 23 битами точности и разрешения без каких-либо таблиц поиска.
Базовый алгоритм основан на рядах Тейлора для sin (x), но сильно изменен, чтобы соответствовать моим намерениям с микроконтроллером AVR и точностью.
Даже то, что использование таблицы 2700 байт (900 записей * 3 байта) было бы привлекательным для скорости, что это за удовольствие или опыт обучения на этом? Конечно, подход CORDIC также рассматривался, может быть, позже, дело здесь в том, чтобы втиснуть Тейлора в ядро AVR и взять воду из сухой породы.
Интересно, может ли Arduino "sin (78.9 °)" запускать Processing (C ++) с точностью 23 бита менее чем за 212us и необходимым кодом, меньшим, чем 205 инструкций. Может быть, если C ++ использует CORDIC. Эскизы Arduino могут импортировать код сборки.
Не имеет смысла размещать код здесь, позже я отредактирую этот пост, добавив на него ссылку, возможно, в моем блоге по этому адресу . Блог в основном на португальском языке.
Это хобби без денег было интересным, раздвигая границы механизма AVR почти 16MIPS на 16 МГц, без инструкции деления, умножение только в 8x8 бит. Это позволяет рассчитать sin (x), cos (x) [= sin (900-x)] и tan (x) [= sin (x) / sin (900-x)].
Помимо всего прочего, это помогло сохранить мой 63-летний мозг полированным и смазанным. Когда подростки говорят, что «старики» ничего не знают о технологиях, я отвечаю: «Подумайте еще раз, кто, по вашему мнению, создал основы для всего, чем вы наслаждаетесь сегодня?».
ура