Литература по вычислению элементарной функции sin с таблицами относится к формуле:
sin(x) = sin(Cn) * cos(h) + cos(Cn) * sin(h)
где x = Cn + h, Cn - константа, для которой sin(Cn) и cos(Cn) были предварительно вычислены и доступны в таблице, и, если следовать методу Gal, Cn был выбран так, чтобы как sin(Cn), так и cos(Cn) близко аппроксимируются числами с плавающей запятой. Величина h близка к 0.0. Примером ссылки на эту формулу является статья (стр. 7).
Я не понимаю, почему это имеет смысл: cos(h), однако он вычисляется, вероятно, будет ошибочным, по меньшей мере, на 0,5 ULP для некоторых значений h, и поскольку он близок к 1.0, это по-видимому, оказывает резкое влияние на точность результата sin(x) при вычислении этого способа.
Я не понимаю, почему приведенная ниже формула не используется:
sin(x) = sin(Cn) + (sin(Cn) * (cos(h) - 1.0) + cos(Cn) * sin(h))
Тогда две величины (cos(h) - 1.0) и sin(h) могут быть аппроксимированы полиномами, которые легко сделать точными, поскольку они дают результаты вблизи нуля. Значения для sin(Cn) * (cos(h) - 1.0), cos(Cn) * sin(h) и для их суммы все еще малы и его абсолютная точность выражается в ULPs малой величины, которую представляет сумма, так что добавление этой величины в sin(Cn) почти правильно округлено.
Мне не хватает чего-то, что делает более раннюю, популярную, более простую формулу тоже хорошо себя вести? Разве авторы считают само собой разумеющимся, что читатели поймут, что первая формула фактически реализована как вторая формула?
EDIT: Пример
Таблица с одной точностью для вычисления одноточечной sinf() и cosf() может содержать следующую точку в одной точности:
         f             |        cos f          |       sin f      
-----------------------+-----------------------+---------------------
0.017967 0x1.2660bcp-6 |    0x1.ffead8p-1      |    0x1.265caep-6
                       |    (actual value:)    |    (actual value:)
                       | ~0x1.ffead8000715dp-1 | ~0x1.265cae000e6f9p-6
Следующие функции представляют собой специализированные функции с одной точностью для использования вокруг 0.017967:
float sinf_trad(float x)
{
  float h = x - 0x1.2660bcp-6f;
  return 0x1.265caep-6f * cos_0(h) + 0x1.ffead8p-1f * sin_0(h);
}
float sinf_new(float x)
{
  float h = x - 0x1.2660bcp-6f;
  return 0x1.265caep-6f + (0x1.265caep-6f * cosm1_0(h) + 0x1.ffead8p-1f * sin_0(h));
}
Тестирование этих функций между 0.01f и 0.025f, по-видимому, показывает, что новая формула дает более точные результаты:
$ gcc -std=c99 test.c && ./a.out relative error, traditional: 2.169624e-07, new: 1.288049e-07 sum of squares of absolute error, traditional: 6.616202e-12, new: 2.522784e-12
Я сделал несколько ярлыков, поэтому просмотрите полную программу.
