GOOGLE ADS

воскресенье, 24 апреля 2022 г.

Эффективное масштабирование с плавающей запятой в C++

Решение проблемы

Возможные улучшения


  • Преобразуйте радианы xв обороты, разделив на 2*pi.


  • Сохраните только дробь, чтобы получился угол (-1,0...1,0). Это упрощает шаг по модулю OP до простого шага «отбросить целое число». Переход к другим угловым единицам просто требует изменения набора коэффициентов. Нет необходимости уменьшать масштаб до радианов.


  • Для положительных значений вычтите 0,5, чтобы получить (-0,5... 0,5), а затем поменяйте знак. Это центрирует возможные значения около 0,0 и обеспечивает лучшую сходимость аппроксимирующего полинома по сравнению с математической функцией синуса. Для отрицательных значений - см. ниже.


  • Вызов my_sin_small1(), который использует этот (-0,5... 0,5) диапазон поворотов, а не [-pi... +pi] радианы.


  • В my_sin_small1(), сложите константы вместе, чтобы удалить corr *шаг.


  • Вместо использования усеченного ряда Тейлора используйте более оптимальный набор. ИМО, это даст лучшие ответы, особенно вблизи +/-pi.



Примечания: Нет кода intтуда/от. floatПри большем анализе можно получить лучший набор коэффициентов, которые фиксируются my_sin(+/-pi)ближе к 0,0. Это всего лишь быстрый набор кода для демонстрации меньшего количества шагов FP и хороших потенциальных результатов.

C-подобный код для OP для переноса на C++

FLOAT my_sin_small1(FLOAT x) {
static const FLOAT A1 = -5.64744881E+01;
static const FLOAT A2 = +7.81017968E+01;
static const FLOAT A3 = -4.11145353E+01;
static const FLOAT A4 = +6.27923581E+00;
const FLOAT x2 = x * x;
return x * (x2 * (x2 * (x2 * A1 + A2) + A3) + A4);
}
FLOAT my_sin1(FLOAT x) {
static const FLOAT pi = 3.141592653589793238462;
static const FLOAT pi2i = 1/(pi * 2);
x *= pi2i;
FLOAT xfraction = 0.5f - (x - truncf(x));
return my_sin_small1(xfraction);
}

Для отрицательных значений используйте -my_sin1(-x)или аналогичный код, чтобы перевернуть знак, или добавьте 0,5 в приведенном выше шаге минус 0,5.

Контрольная работа

#include <math.h>
#include <stdio.h>
int main(void) {
for (int d = 0; d <= 360; d += 20) {
FLOAT x = d / 180.0 * M_PI;
FLOAT y = my_sin1(x);
printf("%12.6f %11.8f %11.8f\n", x, sin(x), y);
}
}

Выход

0.000000 0.00000000 -0.00022483
0.349066 0.34202013 0.34221691
0.698132 0.64278759 0.64255589
1.047198 0.86602542 0.86590189
1.396263 0.98480775 0.98496443
1.745329 0.98480775 0.98501128
2.094395 0.86602537 0.86603642
2.443461 0.64278762 0.64260530
2.792527 0.34202022 0.34183803
3.141593 -0.00000009 0.00000000
3.490659 -0.34202016 -0.34183764
3.839724 -0.64278757 -0.64260519
4.188790 -0.86602546 -0.86603653
4.537856 -0.98480776 -0.98501128
4.886922 -0.98480776 -0.98496443
5.235988 -0.86602545 -0.86590189
5.585053 -0.64278773 -0.64255613
5.934119 -0.34202036 -0.34221727
6.283185 0.00000017 -0.00022483

Альтернативный код ниже дает лучшие результаты около 0,0, но может потребовать немного больше времени. OP кажется более включенным для скорости.

FLOAT xfraction = 0.5f - (x - truncf(x));
// vs.
FLOAT xfraction = x - truncf(x);
if (x >= 0.5f) x -= 1.0f;

Комментариев нет:

Отправить комментарий

Laravel Datatable addColumn returns ID of one record only

Я пытаюсь использовать Yajra Datatable для интеграции DataTable на свой веб-сайт. Я смог отобразить таблицу, но столкнулся с проблемой. В по...