Реализация CCC-библиотеки GCC в системах Debian, по-видимому, представляет собой (IEEE 754-2008) -удобную реализацию функции exp
, подразумевая, что округление всегда будет правильным:
(из Википедии) Стандарт с плавающей запятой IEEE гарантирует, что добавление, вычитание, умножение, деление, плавное многократное добавление, квадратный корень и остаток с плавающей запятой даст корректный округленный результат операции бесконечной точности. В 1985 году такая гарантия не предоставлялась для более сложных функций, и они, как правило, с точностью до последнего бит в лучшем случае. Тем не менее, стандарт 2008 гарантирует, что соответствующие реализации дадут правильно округленные результаты, которые учитывают активный режим округления; Однако выполнение функций не является обязательным.
Оказывается, я сталкиваюсь с ситуацией, когда эта функция действительно затрудняет, потому что точный результат функции exp
часто находится почти точно в середине между двумя последовательными значениями double
(1), а затем программа переносит множество дальнейших вычислений, теряя скорость до 400 (!) в скорости: на самом деле это было объяснением моего (проблемного: -S) вопроса # 43530011.
(1) Точнее, это происходит, когда аргумент exp
оказывается имеющим вид (2 k + 1) × 2 -53 с ka довольно малым целым числом (например, 242 например). В частности, вычисления, участвующие в pow (1. + x, 0.5)
, имеют тенденцию называть exp
таким аргументом, когда x
имеет порядок 2 -44.
Так как реализации правильного округления могут быть настолько трудоемкими в определенных обстоятельствах, я думаю, разработчики также разработали способ получить чуть менее точный результат (скажем, только до 0,6 ULP или что-то вроде этого) за время, которое (грубо) ограничено для каждого значения аргумента в заданном диапазоне... (2)
... Но как это сделать?
(2) Я имею в виду, что я просто не хочу, чтобы некоторые исключительные значения аргумента типа (2 k + 1) × 2 -53 были бы намного более трудоемкими, чем большинство значения того же порядка величины; но, конечно, я не возражаю, если некоторые исключительные значения аргумента идут намного быстрее, или если большие аргументы (по абсолютной величине) нуждаются в большем времени вычисления.
Вот минимальная программа, показывающая явление:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
int main (void)
{
int i;
double a, c;
c = 0;
clock_t start = clock ();
for (i = 0; i < 1e6; ++i) // Doing a large number of times the same type of computation with different values, to smoothen random fluctuations.
{
a = (double) (1 + 2 * (rand () % 0x400)) / 0x20000000000000; // "a" has only a few significant digits, and its last non-zero digit is at (fixed-point) position 53.
c += exp (a); // Just to be sure that the compiler will actually perform the computation of exp (a).
}
clock_t stop = clock ();
printf ("%e\n", c); // Just to be sure that the compiler will actually perform the computation.
printf ("Clock time spent: %d\n", stop - start);
return 0;
}
Теперь после gcc -std=c99 program53.c -lm -o program53
:
$ ./program53
1.000000e+06
Clock time spent: 13470008
$ ./program53
1.000000e+06
Clock time spent: 13292721
$ ./program53
1.000000e+06
Clock time spent: 13201616
С другой стороны, с program52
и program54
(полученным заменой 0x20000000000000
соответственно 0x10000000000000
и 0x40000000000000
):
$ ./program52
1.000000e+06
Clock time spent: 83594
$ ./program52
1.000000e+06
Clock time spent: 69095
$ ./program52
1.000000e+06
Clock time spent: 54694
$ ./program54
1.000000e+06
Clock time spent: 86151
$ ./program54
1.000000e+06
Clock time spent: 74209
$ ./program54
1.000000e+06
Clock time spent: 78612
Остерегайтесь, что это явление зависит от реализации!. По-видимому, среди общих реализаций это проявляется только в системах Debian (включая Ubuntu).
P.-S.: Надеюсь, что мой вопрос не дублируется: я искал аналогичный вопрос полностью без успеха, но, возможно, я заметил, что использовать соответствующие ключевые слова...: -/