Что именно (1.0e300 + pow (2.0, -30.0)> 1.0) делает в STDC? - PullRequest
0 голосов
/ 19 февраля 2019

Я натолкнулся на функцию, которая вычисляет atan(x) (источник здесь ).Сокращая это до сути моего вопроса и слегка переформатируя, они имеют что-то вроде этого:

static const double one   = 1.0,
                   huge   = 1.0e300;

double atan(double x)
{
  /* A lot of uninteresting stuff here */

  if (ix < 0x3fdc0000) {              /* |x| < 0.4375 */

    if (ix < 0x3e200000) {            /* |x| < 2^-29 */
      if ((huge + x) > one) return x; /* raise inexact */
    }

    id = -1;
  }

  /* A lot of more uninteresting stuff */
}

Мне было бы очень интересно, что должна делать строка if ((huge + x) ... и как она работает.

Согласно комментариям, если абсолютное значение x меньше 2^-29, выражение или сравнение вызывает ошибку inexact.

Моя первая проблема заключается в том, что я в настоящее времяне понимаю, почему это так: если вычисление arctan с использованием этой функции приводит к неточным результатам, если абсолютное значение x слишком мало, почему бы им просто не использовать что-то вроде if (fabs(x) < [some_value_here]) ...?Я подозреваю, что это только потому, что предупреждение inexact не будет таким образом поднято в их оборудовании / библиотеке, но я хотел бы знать наверняка.

Предполагая, что я прав, моя вторая проблемав том, что я не понимаю, зачем нужно сравнение.Я думаю, что ключевым моментом здесь является то, что очень маленькое число добавляется к очень большому, так что это добавление не меняет большое число в достаточной степени или даже не меняет вообще.Следовательно, это добавление вызовет предупреждение inexact, а не сравнение.Поэтому я спрашиваю себя, что должно делать сравнение.Это просто для того, чтобы заставить компилятор фактически вычислять (huge + x), который в противном случае мог бы быть оптимизирован?

Наконец, я был бы благодарен, если бы кто-нибудь немного объяснил математику.Выбор значения 1.0e300 для huge кажется довольно произвольным выбором.Но это только бонусный вопрос, так как я признаю, что я еще не выполнил часть math своей домашней работы (я не новичок в отношении значений double и их представления IEEE754, но понимаю математические аспектыэтот код займет у меня некоторое время, если кто-то не даст короткого объяснения).

РЕДАКТИРОВАТЬ 1

Только что случайно видел:

The float32 версия этой функции, включая странную строку, рассмотренную выше, почти буквально все еще в glibc 2.19!Поскольку glibc должен быть переносимым, этот код также должен быть.Он находится в подкаталоге sysdeps\ieee754\flt-32, поэтому я полагаю, что это программная эмуляция функций float32, где переносимость не является проблемой, потому что аппаратно-зависимые странности не будут отображаться (я думаю, что программная эмуляция порождает эти исключенияточно так, как определено в IEEE754).

1 Ответ

0 голосов
/ 19 февраля 2019

Цель if ((huge + x) > one) return x; - создать неточное исключение с плавающей точкой и затем вернуться из процедуры.

Исключение с плавающей точкой не является ловушкой или обработчиком.Это просто означает, что в операции с плавающей точкой произошло нечто необычное.Что происходит потом, зависит от обстоятельств операции.В частности, среда с плавающей запятой может быть установлена ​​таким образом, чтобы неточное исключение просто поднимало флаг в специальном регистре и продолжало операцию, предоставляя числовой результат.Или это может быть установлено так, что неточное исключение вызывает прерывание, и управление программой перенаправляется обработчику прерываний.

Этот код, реализующий atan, не знает, как устанавливается среда с плавающей запятой.Может быть, он может получить настройки, но не хочет беспокоиться об этом.Учитывая, что она решила, что функция арктангенса не может быть точно вычислена, самый простой способ вызвать неточное исключение с плавающей запятой - просто выполнить простое сложение, которое дает неточный результат.Это неточное сложение будет иметь то же поведение, которое желательно для неточного арктангенса - оно будет либо просто поднимать флаг, либо вызывать ловушку, в зависимости от настроек.

Что касается сравнения с ix < 0x3e200000,это не ясно.С одной стороны, ix был откорректирован для отражения абсолютного значения, тогда как x - нет, так почему бы не использовать уже подготовленный ix вместо использования другой операции для получения fabs(x)?Кроме того, целочисленное сравнение обычно требует меньше ресурсов процессора, чем сравнение с плавающей запятой, особенно в процессорах того времени, когда был написан этот код.Или, может быть, автор просто использовал один над автором, возможно, написав большую часть своего кода, используя ix для работы с кодировкой с плавающей точкой, а не x для работы со значением с плавающей точкой, и они сделалине хочу переключаться туда-сюда без необходимости.Это также может быть связано с тем, что код был написан до того, как стала доступна шестнадцатеричная запись с плавающей запятой (поэтому мы могли написать x < 0x1p-29f), а компиляторы не были хороши в преобразовании десятичных чисел в значения с плавающей запятой, поэтому они не хотели писатьиз значения с плавающей запятой в исходном коде.

Этот вид кода проблематичен и сильно зависит от реализации C, для которой он написан.Как правило, компилятор не может гарантировать, что (huge + x) > one будет оцениваться во время выполнения программы.Компилятор может оценить его во время компиляции.Предположительно, хотя, если этот код написан для конкретной реализации C, они знают, что компилятор или оценит его во время компиляции, или обеспечит достижение того же результата, включая повышение неточного исключения с плавающей точкой.

На первый взгляд, (huge + x) > one, похоже, не делает ничего полезного, чего не делает только huge + x, но, возможно, автор знал что-то о компиляторе, чего нет у нас.

huge делаетне должно быть 1.0e300.Любое значение, настолько большое, что сумма huge и x не может быть точной, будет достаточным.

...