Когда функция триггера с аргументом степени должна возвращать -0.0?


10

При создании Триг функций my_sind(d), my_cosd(d), my_tand(d), что использовали градусный аргумент , а не радиан один и давал точные ответы на кратные 90, я заметил , что результат был иногда -0.0вместо 0.0.

my_sind( 0.0) -->  0.0
my_sind(-0.0) --> -0.0

my_sind(180.0) --> -0.0
my_sind(360.0) -->  0.0

sin()и tan()обычно возвращают один и тот же знак нулевого результата для данного знака нулевого ввода. Имеет смысл, что my_sin()должно соответствовать sin()этим входам.

my_sind( 0.0) alike sin( 0.0) -->  0.0
my_sind(-0.0) alike sin(-0.0) --> -0.0

Вопрос заключается в том : для того, что целое число non_zero_nдолжно / может результат когда - либо возвращаться -0.0к my_sind(180*non_zero_n), my_cosd(180*n + 180), my_tand(180*non_zero_n)?

Это достаточно просто для кодирования, так что только f(-0.0)производит -0.0и делает с ним. Просто интересно, есть ли какая-либо причина, чтобы сделать другое f(x) возвращение -0.0для любого другого ( отличного от нуля ) xи важности страхования этого знака.


Примечание: Это не вопрос о том, почему 0.0против -0.0происходит. Это не то, почему cos(machine_pi/4)не возвращается 0.0. Это также не вопрос о том, как контролировать поколение 0.0или -0.0. Я вижу это лучше всего как вопрос дизайна.

Ответы:


4

Принцип дизайна «наименьшего удивления» предполагает, что мы обращаемся к ранее установленной функциональности для руководства. В этом случае, ближайший установлено функциональность обеспечивается sinpiи cospiфункции введены в IEEE Std 754-2008 (Стандарт IEEE Standard для арифметики с плавающей точкой), раздел 9. Эти функции не являются частью действующих стандартов ISO C и ISO C ++, но были включены в математические библиотеки различных платформ программирования, например, CUDA.

Эти функции вычисляют sin (πx) и cos (πx), где умножение на π происходит неявно внутри функции. tanpiне определен, но, исходя из математической эквивалентности, можно предположить, что он обеспечивает функциональность в соответствии с tanpi(x) = sinpi(x) / cospi(x).

Теперь мы можем определить sind(x) = sinpi(x/180), cosd(x) = cospi(x/180), tand(x) = tanpi(x/180)интуитивным образом. Раздел 9.1.2 IEEE-754 описывает обработку специальных аргументов для sinpiи cospi. В частности:

sinPi (+ n) равно +0, а sinPi (−n) равно −0 для натуральных чисел n. Это подразумевает, что при соответствующих режимах округления sinPi (-x) и -sinPi (x) имеют одинаковое число (или оба NaN) для всех x. cosPi (n + ½) равно +0 для любого целого числа n, когда n + ½ представимо.

Стандарт IEEE 754-2008 не дает обоснования для приведенных требований, однако в предварительном проекте соответствующего раздела говорится:

Если значение функции равно нулю, знак этого 0 лучше всего определить, учитывая непрерывное расширение функции знака математической функции.

Изучение почтового архива 754 Рабочей группы может дать дополнительную информацию, у меня не было времени, чтобы копаться в нем. Реализуя sind(), cosd()и, tand()как описано выше, мы затем приходим к этой таблице примеров:

SIND
 angle value 
  -540 -0
  -360 -0
  -180 -0
     0  0
   180  0
   360  0
   540  0

COSD
 angle value
  -630  0
  -450  0
  -270  0
   -90  0
    90  0
   270  0
   450  0

TAND
 angle value
  -540  0
  -360 -0
  -180  0
     0  0
   180 -0
   360  0
   540 -0

5

sin () и tan () обычно возвращают один и тот же знак нулевого результата для данного знака нулевого ввода

Это может быть в целом верно, так как:

  • Скорость / точность . Для достаточно маленьких двойников лучший ответ для sin(x)- x. То есть для чисел, меньших, чем примерно 1.49e-8, ближайший двойник к синусу x на самом деле сам x (см. Исходный код glibc для sin () ).

  • Обработка особых случаев .

    На несколько необычных арифметических операций влияет знак нуля; например "1/(+0) = +inf"но "1/(-0) = -inf". Чтобы сохранить свою полезность, знаковый бит должен распространяться через определенные арифметические операции в соответствии с правилами, вытекающими из соображений непрерывности.

    Ожидается, что реализации элементарных трансцендентных функций, таких как sin (z) и tan (z), а также их обратных и гиперболических аналогов, хотя они не определены стандартами IEEE, будут следовать аналогичным правилам. Ожидается, что реализация sin(z) будет воспроизводить знак, z а также его значение вz = ±O .

    ( Отрезки ветвей для сложных элементарных функций или много шума из ничего не значащего У. Кахана)

    Ноль с отрицательным 1 / sin(x)знаком повторяет концепцию математического анализа приближения к 0 снизу как одностороннего предела (учтите : знак нуля имеет огромное значение).

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

Учитывая второй пункт, я бы написал my_sindтак:

my_sind(-0.0) is -0.0
my_sind(0.0) is 0.0

В последнем стандарте C (F.10.1.6 sinи F.10.1.7 tan, реализации со знаком ноль) указывается, что если аргумент равен ±0, он возвращается неизмененным .

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

Для других значений я думаю, что это вопрос приближения. Дано M_PI<π:

0 = sin(π) < sin(M_PI)  1.2246467991473532e-16  +0.0
0 = sin(-π) > sin(-M_PI)  -1.2246467991473532e-16  -0.0
0 = sin(2*π) > sin(2*M_PI)  -2.4492935982947064e-16
0 = sin(-2*π) < sin(-2*M_PI)  2.4492935982947064e-16

Так что, если my_sindдает точные ответы с кратностью 180 °, он может вернуться +0.0или -0.0(я не вижу четкой причины, чтобы предпочесть одно над другим).

Если my_sindиспользуется некоторая аппроксимация (например, degree * M_PI / 180.0формула преобразования), следует учитывать, как она приближается к критическим значениям.


Что вы думаете по поводу sind(180), sind(-180), sind(360), sind(-360),...?
Чукс - Восстановить Монику

Спасибо за обновления. Возможно мой пост не понятен. Главный вопрос, должен ли my_trig(x)когда-нибудь вернуться, -0.0когда |x|нет 0.0?
Чукс - Восстановить Монику

Спасибо за «Так что, если my_sind дает точные ответы с кратностью 180 °, он может вернуть +0.0 или -0.0 (я не вижу четкой причины, чтобы предпочесть одно другому)». Пока это самый близкий предмет для обсуждения. Я думаю, что «принцип наименьшего удивления» побуждает всегда возвращаться +0.0, но я смотрю, есть ли веские причины для возвращения -0.0в некоторых ситуациях (кроме x == +/-0.0).
chux - Восстановить Монику

@chux: Я думаю, что для кратных 180.0действительно нужно изучить значения относительной точности машины, учитывая эти значения. То есть наименьшее увеличение / уменьшение, которое дает другое представимое значение в этом числовом формате. Затем сравните это значение с истинным значением, чтобы увидеть, упадет ли оно на плюс или минус.
rwong

@rwong Спасибо за идею. Кратные 90,0 градусов , то точные sind(double degrees) и cosd(double degrees)значение может быть возвращено: -1.0, +0.0, +1.0. Этот пост должен -0.0быть возвращен (кроме sind (-0.0)). Примечание: sind()это не использовать упрощенный sin(x/360*M_PI)подход.
chux - Восстановить Монику

3

Библиотека не пытается отличить +0 от -0. IEEE 754 беспокоится об этом различии ... Я нашел функции [в math.h] достаточно сложными, чтобы писать, не беспокоясь о знаке ничто. - PJ Plauger, The Standard C Library , 1992, стр. 128.

Формально тригогенные функции должны возвращать знак нуля в соответствии со стандартом C ... что оставляет поведение неопределенным.

Перед лицом неопределенного поведения принцип наименьшего удивления предполагает дублирование поведения соответствующей функции из math.h. Это пахнет оправданно, в то время как отклонение от поведения соответствующей функции в math.hзапахах похоже на способ введения ошибок именно в код, который зависит от знака нуля.


Функции триггера в math.hне возвращают 0.0, если даны такие аргументы, как +/- pi / 2 или +/- pi, поскольку эти функции могут принимать представимые значения только вблизи +/- pi / 2 и т. Д. Эти «близкие» значения возвращают результаты около 0,0. Поскольку функции триггера библиотеки std ( sin cos tan) не возвращают 0.0 (или -0.0) для любого ввода (кроме +/- 0.0), но my_sind (), my_cosd (), my_tand () могут возвращать 0.0 (или -0.0), то есть нет поведения 0.0 для дублирования.
Чукс - Восстановить Монику

@chux Предпосылка, которая sin(-0.0)должна вернуться, -0является подозрительной. Он рассматривает детали реализации стандарта IEEE как тригонометрический принцип. Хотя существует общий математический принцип нуля как предела двух интервалов, воплощенный в реализации IEEE, он происходит на том уровне абстракции, не входящем в общую тригонометрию [отсюда и изменчивость того, что возвращают ваши тригонометрические функции]. Лучшее, что может произойти, это то, что вы можете определить произвольное соглашение, но оно будет отличаться от math.hнебрежности по знаку нуля.
Бен Руджерс

Примечание: я не предлагаю sin(-0.0)возвращаться -0.0, но это my_sind(x)должно совпадать, sin(x)когда xесть +/-0.0. IOW: следуйте предыдущей практике. Кроме того, сам вопрос заключается в том, что делать, когда x != 0.0, my_sind(x)когда-либо должен вернуться, -0.0как my_sind(180)и т.д.? Может быть, ваш ответ / комментарий говорит об этом - но я этого не видел.
chux - Восстановить Монику

@chux Если поведение не определено, то оно не определено. Это просто так С. Plauger не беспокоиться о +0сравнении , -0когда он писал math.hдвадцать лет назад. Мне не ясно, какую проблему решает твое беспокойство по поводу разницы.
Бен Руджерс

1
Надеюсь, вы видите, что для хорошо реализованного sin(rad)для любого значения rad>0и любой точности никогда не даст, 0.0как пи иррационально. [Ref] (www.csee.umbc.edu/~phatak/645/supl/Ng-ArgReduction.pdf) Однако my_sind(deg)дает точное 0.0(или + или -) каждое кратное, так 180.0как значение 0.0 является правильным математическим результатом. «Принцип наименьшего удивления» предлагает вернуть 0.0 в этих случаях. Мой вопрос должен -0.0быть возвращен в этих случаях?
chux - Восстановить Монику
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.