Функция греха Apple libm

Этот вопрос касается реализации функции sin в математической библиотеке Apple libm C, доступной здесь . Основная функция sin начинается в строке 736. Короче говоря: когда x настолько мало, что ближайшее к sin(x) число с плавающей запятой равно x, почему функция выполняет умножение с плавающей запятой, результат которого никогда не используется, а затем возвращает x вместо того, чтобы просто немедленно возвращать x?

Функция использует разные методы в зависимости от величины xk входных данных x. Меня интересует случай, когда xk настолько мал, что ближайшее к sin(x) число с плавающей запятой составляет всего x, строки 809-829. Я бы предположил, что в этом случае функция просто выполнила бы return x. Вот что он делает вместо этого:

/*  Make t0 volatile to force compiler to fetch it at run-time
    rather than optimizing away the multiplication.
*/
static volatile const double t0 = 1/3.;

/*  Get t0 once.  If we wrote "t0*t0", the compiler would load it
    twice, since it is volatile.
*/
const double t1 = t0;

/*  Perform a multiplication and pass it into an assembly construct to
    prevent the compiler from knowing we do not use the result and
    optimizing away the multiplication.
*/
__asm__("" : : "X" (t1*t1));

// Return the floating-point number nearest sine(x), which is x.
return x;

Таким образом, он создает новый статический изменчивый const double t0, равный 1/3., другой const double t1 с тем же значением, умножает t1 на себя так, как он утверждает, что компилятор не может оптимизировать... затем возвращает x. Почему он делает это, а не просто возвращает x в начало?

Дикая догадка: возможно, для предотвращения атак по побочным каналам времени, когда функция используется в контекстах, связанных с безопасностью?

Ctx 16.04.2024 18:25

@Ctx Я подумал об этом, но не думаю, что это правильно. Помимо всего остального, это просто математическая библиотека общего назначения.

Matthew Towers 16.04.2024 18:32
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

… почему функция выполняет умножение с плавающей запятой, результат которого никогда не используется, а затем возвращает x

Операция умножения с плавающей запятой имеет два выхода. Одним из них является числовое значение в регистре с плавающей запятой, а другим — обновленные флаги в битах состояния с плавающей запятой. В том месте кода, о котором вы спрашиваете, x настолько мало, что ближайшее к sin x представимое значение равно x, поэтому оно будет возвращаемым значением функции. Однако синус x не совсем x, поэтому мы хотели бы поднять флаг неточного. Умножение 1/3. само по себе достигает этого.

Вы заметите аналогичную заботу в return x * (1 - 0x1p-53); в строке 803. В этот момент известно, что x находится в субнормальном диапазоне (здесь написано «ненормально», но более уместно «субнормально»), поэтому x * (1 - 0x1p-53) имеет, из-за округления, то же самое. значение как x. Но это также поднимает флаг неточной точности, если x не равно нулю. (А синус нуля точен, поэтому лучше не поднимать флаг неточной точности, когда x равен нулю.)

Я думаю, ты понял, спасибо. Они включают в себя заявление об отказе от ответственности «может или не может быть точным, даже если результат точен», но, похоже, они прилагают некоторые усилия, чтобы сделать это правильно в двух самых маленьких случаях.

Matthew Towers 16.04.2024 18:38

@MatthewTowers: «Они» — это я. Я написал эту программу.

Eric Postpischil 16.04.2024 18:38

черт возьми. Мы очень ценим ваши обширные комментарии в источнике.

Matthew Towers 16.04.2024 18:43

@EricPostpischil Вызвал бы внутренний эквивалент fesetexcept(FE_INEXACT) то же самое или это более тонко?

Ian Abbott 16.04.2024 18:56

@IanAbbott: Я думаю, что все по-другому; fesetexcept только поднимает флаг. Выполнение фактической операции с плавающей запятой с неточным результатом не только поднимает флаг, но и вызывает исключение, если оно включено для неточных результатов. В любом случае, простая загрузка подготовленного значения и инструкции умножения с плавающей запятой — это практически самый быстрый способ поднять флаг.

Eric Postpischil 16.04.2024 18:59

Я подозреваю, что sin(x) неточно для всех значений x радиан, кроме x == +/- 0. Теперь меня интересует попытка правильно установить биты состояния с плавающей запятой для sine_grades(x), поскольку это дает точные рациональные результаты при 0 +180°*n, +/-30 +180°*n. Раньше я выступал за уменьшение угла в восьмых долях (360°/8), затем вызов поддиапазона sine_grades_45(x) и последующее построение ответа. Я вижу, что для достижения правильных битов состояния с плавающей запятой коду может потребоваться сначала уменьшить угол на двенадцатые доли (360 °/12) (или, может быть, на шестые). Спасибо за понимание построения функций.

chux - Reinstate Monica 17.04.2024 19:28

@chux-ReinstateMonica: Да, sin не имеет рациональных значений аргументов (представимых значений), кроме нуля. Таким образом, это будет неточно, за исключением нуля, NaN и ±∞. (Это вызывает недопустимость сигнализации NaN или ±∞). Утверждение, что это может или не может привести к неточным результатам, даже если результат точен, по сути, является частью спецификации шаблона для большинства подпрограмм математической библиотеки. Это более применимо, например, в log10, где log10(1e6) может выполнить некоторые вычисления и дать точный результат 6, но промежуточные вычисления будут неточными.

Eric Postpischil 18.04.2024 01:26

@chux-ReinstateMonica: Поскольку sin всегда вызывает неточные результаты для конечных аргументов, отличных от нуля, от этого нет особого смысла. Любой, кто звонит sin, обычно должен ожидать неточного ответа. Однако гипотетически это полезно для тех, кто пытается выполнить точные вычисления и вызывает библиотечную процедуру, которая, по его мнению, может быть точной, но вызывает внутренний вызов sin. Как правило, стандартные подпрограммы математической библиотеки рассматривались как вещи, которые мы хотели разумно уважать семантику с плавающей запятой. Для людей, которым нужна скорость, есть библиотеки Vector/SIMD, которые могут игнорировать флаги и тому подобное.

Eric Postpischil 18.04.2024 01:30

Другие вопросы по теме