Коли триггерна функція зі ступенем аргументу повинна повертати -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 .

    ( Відсічки відгалужень для складних елементарних функцій або багато чого про біт знака У. Кахана)

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

EDIT

Розглядаючи другий пункт, я б написав 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, він повертається немодифікованим .

EDIT 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),...?
chux

Дякуємо за оновлення. Можливо, мій пост незрозумілий. Головне питання - чи варто my_trig(x)коли-небудь повертатися, -0.0коли |x|його немає 0.0?
chux

Дякую за те, що "якщо 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 дуже непокоїть цю відмінність ... Я знайшов функції [в математиці.h] досить важкими, щоб писати, не роздумуючи про знак нічого. - PJ Plauger, The Standard C Library , 1992, стор. 128.

Формально функції тригу повинні повернути знак нуля відповідно до стандарту С ..., що залишає поведінку невизначеною.

В умовах невизначеної поведінки принцип найменшого здивування пропонує дублювати поведінку відповідної функції 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

@chux Приміщення, яке sin(-0.0)має повернутися, -0є підозрюваним. Він розглядає деталі реалізації стандарту IEEE як тригонометричний принцип. Хоча існує загальний математичний принцип нуля, як межа двох інтервалів, втілених в реалізації IEEE, він відбувається на тому рівні абстракції, що не знаходиться в межах загальної тригонометрії [звідси мінливість повернення ваших тригонометричних функцій]. Найкраще, що може статися, - це те, що ви можете визначити довільну умову, але вона буде розходитися від math.hнезбалансованості Росії над знаком нуля.
ben rudgers

Примітка. Я не пропоную 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 Якщо поведінка не визначена, то вона не визначена. Ось так і є. Плаж не хвилювався +0проти того, -0коли писав math.hдвадцять років тому. Мені незрозуміло, яку проблему вирішує ваша роздумка про різницю.
ben rudgers

1
Сподіваємось, ви бачите, що добре виконане sin(rad)для будь-якої цінності rad>0та будь-якої точності ніколи не вийде, 0.0оскільки pi нераціонально. [Посилання] (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.