Kiedy funkcja trig z argumentem stopnia powinna zwracać wartość -0,0?


10

W tworzeniu funkcji parametrów wyzwalania my_sind(d), my_cosd(d), my_tand(d), że użył argumentu stopni zamiast radianie jedną i pod warunkiem dokładnych odpowiedzi na wielokrotność 90, zauważyłem, że wynik był czasami -0.0zamiast 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()i tan()zazwyczaj zwraca ten sam wynik zero znaku dla danych wejściowych zero znaku. Ma to sens, że my_sin()powinno pasować sin()do tych danych wejściowych.

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

Pytanie brzmi : co cały numer non_zero_npowinien / może wynikiem kiedykolwiek powrócić -0.0do my_sind(180*non_zero_n), my_cosd(180*n + 180), my_tand(180*non_zero_n)?

Kodowanie jest wystarczająco łatwe, więc f(-0.0)produkuje się -0.0i wykonuje się je. Po prostu zastanawiasz się, czy jest jakiś powód, aby dokonać innego f(x) zwrotu -0.0za jakikolwiek inny ( niezerowy ) xi znaczenie ubezpieczenia tego znaku.


Uwaga: To nie jest pytanie, dlaczego występuje 0.0vs. -0.0Nie dlatego cos(machine_pi/4)nie wraca 0.0. Nie jest to również pytanie, jak kontrolować generowanie 0.0lub -0.0. Uważam to najlepiej za pytanie projektowe.

Odpowiedzi:


4

Zasada projektowania „najmniejszego zaskoczenia” sugeruje, że szukamy wskazówek dla wcześniej ustalonej funkcjonalności. W tym przypadku, najbliżej założona funkcjonalność jest dostarczana przez sinpii cospifunkcje wprowadzone w IEEE Std 754-2008 (IEEE Standard dla arytmetyki zmiennoprzecinkowej), rozdział 9. Funkcje te nie są częścią bieżących ISO C i C ++ ISO standardów, ale zostały włączone do bibliotek matematycznych różnych platform programistycznych, na przykład CUDA.

Funkcje te obliczają sin (πx) i cos (πx), gdzie mnożenie przez π występuje niejawnie wewnątrz funkcji. tanpinie jest zdefiniowany, ale można założyć, że w oparciu o równoważność matematyczną zapewnia funkcjonalność zgodnie z tanpi(x) = sinpi(x) / cospi(x).

Możemy teraz zdefiniować sind(x) = sinpi(x/180), cosd(x) = cospi(x/180), tand(x) = tanpi(x/180)w intuicyjny sposób. Sekcja 9.1.2 IEEE-754 określa obsługę specjalnych argumentów za sinpii cospi. W szczególności:

sinPi (+ n) wynosi +0, a sinPi (-n) wynosi -0 dla dodatnich liczb całkowitych n. To implikuje, przy odpowiednich trybach zaokrąglania, że ​​sinPi (−x) i −sinPi (x) mają tę samą liczbę (lub oba NaN) dla wszystkich x. cosPi (n + ½) wynosi +0 dla dowolnej liczby całkowitej n, gdy n + ½ jest reprezentowalne.

Norma IEEE 754-2008 nie podaje uzasadnienia dla przytoczonych wymagań, jednak wczesny szkic odpowiedniej sekcji stwierdza:

Jeśli wartość funkcji wynosi zero, znak tego 0 najlepiej jest określić, biorąc pod uwagę ciągłe rozszerzanie funkcji znaku funkcji matematycznej.

Przejrzenie archiwum poczty grupy roboczej 754 może dostarczyć dodatkowych informacji, nie miałem czasu go przekopać. Wykonawczych sind(), cosd()i tand(), jak opisano powyżej, a następnie osiągnąć tabeli przykładowych przypadkach:

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 () i tan () zwykle zwracają ten sam wynik zero znaku dla danych wejściowych zero znaku

Może być ogólnie prawdą, ponieważ:

  • Szybkość / dokładność . W przypadku wystarczająco małych zakładów podwójnych najlepszą odpowiedzią sin(x)jest x. To znaczy, dla liczb mniejszych niż około 1.49e-8, najbliższą podwójną sinusoidą x jest tak naprawdę sam x (zobacz kod źródłowy glibc dla sin () ).

  • Postępowanie w szczególnych przypadkach .

    Na kilka nadzwyczajnych operacji arytmetycznych ma wpływ znak zerowy; na przykład "1/(+0) = +inf"ale "1/(-0) = -inf". Aby zachować swoją użyteczność, bit znaku musi propagować poprzez pewne operacje arytmetyczne zgodnie z regułami wynikającymi z rozważań dotyczących ciągłości.

    Realizacje elementarnych funkcji transcendentalnych, takich jak sin (z) i tan (z) oraz ich inwersje i hiperboliczne analogi, choć nie określone w standardach IEEE, powinny podlegać podobnym zasadom. sin(z) Oczekuje się, żezz = ±O wdrożenie spowoduje odtworzenie znaku oraz jego wartości w .

    ( Cięcia gałęzi dla złożonych funkcji elementarnych lub wiele hałasu o znak Nothing Bit autorstwa W. Kahana)

    Ujemnie podpisane zero przypomina koncepcję analizy matematycznej polegającej na zbliżeniu się 0 od dołu jako jednostronnego limitu (rozważ 1 / sin(x): znak zerowy robi ogromną różnicę).

EDYTOWAĆ

Biorąc pod uwagę drugi punkt, napisałbym my_sindtak, aby:

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

Najnowszy standard C (F.10.1.6 sini F.10.1.7 tan, implementacje ze znakiem zero) określa, że ​​jeśli argument jest ±0, to jest zwracany bez zmian .

EDYCJA 2

W przypadku innych wartości myślę, że jest to kwestia przybliżenia. Biorąc pod uwagę 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

Więc jeśli my_sindpoda dokładne odpowiedzi w wielokrotnościach 180 °, może się zwrócić +0.0lub -0.0(nie widzę wyraźnego powodu, aby preferować jedno od drugiego).

Jeśli my_sindstosuje jakieś przybliżenie (np. degree * M_PI / 180.0Formułę konwersji), powinno rozważyć, w jaki sposób zbliża się do wartości krytycznych.


O czym myślisz sind(180), sind(-180), sind(360), sind(-360),...?
chux - Przywróć Monikę

Dziękuję za aktualizację. Może mój post nie jest jasny. Główne pytanie brzmi powinny my_trig(x)kiedykolwiek wrócić -0.0, gdy |x|nie jest 0.0?
chux - Przywróć Monikę

Dzięki za „Więc jeśli my_sind podaje dokładne odpowiedzi przy wielokrotności 180 °, może zwrócić +0,0 lub -0,0 (nie widzę wyraźnego powodu, aby preferować jedną z drugiej).” To jak dotąd najbliższy punkt dyskusji. Myślę, że ta „zasada najmniejszego zdziwienia” zachęca do ciągłego powrotu +0.0, ale szukam przekonania, czy istnieją ważne powody do powrotu -0.0w niektórych sytuacjach (innych niż x == +/-0.0).
chux - Przywróć Monikę

@chux: Myślę, że w przypadku wielokrotności 180.0naprawdę trzeba sprawdzić wartości względnej precyzji maszyny, biorąc pod uwagę te wartości. Oznacza to, że najmniejszy przyrost / spadek, który daje inną reprezentatywną wartość w tym formacie liczbowym. Następnie porównaj tę wartość z wartością prawdziwą, aby zobaczyć, czy spadnie ona na stronę dodatnią czy ujemną.
rwong

@rwong Dzięki za pomysł. Wielokrotności 90,0 stopni The dokładna sind(double degrees) i cosd(double degrees)wartość mogą być zwrócone: -1.0, +0.0, +1.0. Ten post powinien -0.0zostać kiedykolwiek zwrócony (oprócz sind (-0.0)). Uwaga: sind()nie nie skorzystać z uproszczonego sin(x/360*M_PI)podejścia.
chux - Przywróć Monikę

3

Biblioteka nie próbuje odróżnić +0 od -0. IEEE 754 trochę martwi się tym rozróżnieniem ... Znalazłem funkcje [w mat. H] dość trudne do napisania bez obaw o znak nicości. - PJ Plauger, The Standard C Library , 1992, str. 128.

Formalnie, funkcje trig powinny zwracać znak zero zgodnie ze standardem C ... co pozostawia zachowanie niezdefiniowane.

W obliczu nieokreślonego zachowania zasada najmniejszego zdziwienia sugeruje powielenie zachowania odpowiedniej funkcji z math.h. Pachnie to uzasadnieniem, jednocześnie odbiegając od zachowania odpowiedniej funkcji w math.hzapachach, jak sposób wprowadzenia błędów do dokładnie kodu, który zależy od znaku zera.


Funkcje wyzwalające math.hnie zwracają 0,0, gdy podane są argumenty takie jak +/- pi / 2 lub +/- pi, ponieważ te funkcje mogą przyjmować reprezentowalne wartości w pobliżu +/- pi / 2 itp. Te „bliskie” wartości zwracają wyniki blisko 0,0. Ponieważ funkcje sin cos tanwyzwalające biblioteki std ( ) nie zwracają 0,0 (lub -0,0) dla żadnego wejścia (z wyjątkiem +/- 0,0), ale my_sind (), my_cosd (), my_tand () mogą zwracać 0,0 (lub -0,0) brak zachowania 0.0 do powielenia.
chux - Przywróć Monikę

@chux Założeniem, które sin(-0.0)powinno zwrócić, -0jest podejrzane. Traktuje szczegół implementacji standardu IEEE jako zasadę trygonometryczną. Chociaż istnieje ogólna zasada matematyczna zerowa jako granica dwóch przedziałów zawarta w implementacji IEEE, występuje ona na tym poziomie abstrakcji poza ogólną trygonometrią [stąd zmienność powrotu funkcji trygonometrycznych]. Najlepsze, co może się zdarzyć, to zdefiniowanie arbitralnej konwencji, ale różni się ona od math.hnonszalancji ponad znakiem zera.
ben rudgers

Uwaga: nie sugeruję , że powinien sin(-0.0)wrócić -0.0, ale my_sind(x)powinien pasować, sin(x)kiedy xjest +/-0.0. IOW: postępuj zgodnie z poprzednią praktyką. Co więcej, samo pytanie dotyczy bardziej tego, co zrobić, kiedy x != 0.0powinien my_sind(x)powrócić -0.0jak w my_sind(180)itp.? Być może twoja odpowiedź / komentarz dotyczy tego - ale tego nie widziałem.
chux - Przywróć Monikę

@chux Jeśli zachowanie jest niezdefiniowane, to jest niezdefiniowane. Taki właśnie jest C. Plaugera nie martwić +0kontra -0kiedy pisał math.hdwadzieścia lat temu. Nie jest dla mnie jasne, jaki problem rozwiązuje twój niepokój związany z różnicą.
ben rudgers

1
Mamy nadzieję, że widzisz, że dla dobrze zaimplementowanej sin(rad)dowolnej wartości rad>0i dowolnej precyzji nigdy nie da się, 0.0ponieważ pi jest irracjonalne. [Ref] (www.csee.umbc.edu/~phatak/645/supl/Ng-ArgReduction.pdf) Jednak my_sind(deg)daje dokładną 0.0(albo + lub -) każdą wielokrotność, 180.0ponieważ wartość 0,0 jest poprawnym wynikiem matematycznym. „Zasada najmniejszego zdziwienia” sugeruje zwrócenie 0,0 w tych przypadkach. -0.0W takich przypadkach moje pytanie powinno zostać zwrócone?
chux - Przywróć Monikę
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.