Dlaczego zero ujemne jest ważne?

64

Jestem zdezorientowany, dlaczego dbamy o różne reprezentacje dla dodatniego i ujemnego zera.

Z niejasnych wspomnień czytam, że posiadanie ujemnej reprezentacji zera jest niezwykle ważne w programowaniu, które obejmuje liczby zespolone. Nigdy nie miałem okazji pisać kodu zawierającego liczby zespolone, więc jestem nieco zaskoczony, dlaczego tak się dzieje.

Artykuł Wikipedii na temat tego pojęcia nie jest szczególnie pomocny; tylko niejasne twierdzenia o podpisanym zeru upraszczają niektóre operacje matematyczne w liczbach zmiennoprzecinkowych, jeśli dobrze rozumiem. W tej odpowiedzi wymieniono kilka funkcji, które zachowują się inaczej. Być może z przykładów można wywnioskować, jeśli znasz sposoby ich użycia. (Chociaż szczególny przykład złożonych pierwiastków kwadratowych wydaje się zupełnie niewłaściwy, ponieważ te dwie liczby są matematycznie równoważne, chyba że mam nieporozumienie.) Ale nie byłem w stanie znaleźć jasnego stwierdzenia o rodzaju kłopotów, które można spotkać, gdyby ich nie było. Im więcej zasobów matematycznych udało mi się znaleźć, stwierdziłem, że nie ma między nimi rozróżnienia z matematycznego punktu widzenia, a artykuł z Wikipedii wydaje się sugerować, że rzadko jest to postrzegane poza obliczeniami, poza opisywaniem ograniczeń.

Dlaczego więc zero ujemne jest cenne w informatyce? Jestem pewien, że coś mi umknęło.

jpmc26
źródło
6
Ujemne zero może sygnalizować niedopełnienie liczby zmiennoprzecinkowej IEEE, ale poza tym jej użycie wydaje się kontrowersyjne i niejasne. Gdybym miał zgadywać, powiedziałbym, że zero ujemne jest reprezentowane w zmiennoprzecinkowym punkcie IEEE, ponieważ ... cóż, możesz . Aby uzyskać jeszcze bardziej interesującą jazdę, sprawdź informacje o sygnale zmiennoprzecinkowym NaN.
Robert Harvey
1
Jeśli konkretny przykład to „1 / 0,0” / „1 / -0,0”, 0 oznacza cięcie gałęzi dla 1 / x, a limit zależy od tego, czy podejdziesz do niego od dołu, czy powyżej.
Vatine
@Vatine Nie, szczególnym przykładem jest sqrt(-1+0i) = ii sqrt(-1-0i) = -i, choć uważam, że ma odpowiednią składnię dla jakiegoś języka programowania. Będę edytować, aby być bardziej przejrzystym.
jpmc26
3
Szukałem programistów , przepełnienia stosu , informatyki , matematyki i inżynierii . Jedyne pytanie, jakie mogłem znaleźć, dotyczy zastosowania wartości zmiennoprzecinkowej ujemnego zera? . To nie może być dopiero drugi raz!
Jestem naprawdę zaskoczony, że liczby całkowite wcale nie pojawiły się w odpowiedziach, zwłaszcza biorąc pod uwagę przykład pierwiastka kwadratowego.
jpmc26

Odpowiedzi:

69

Należy pamiętać, że w arytmetyce FPU 0 niekoniecznie musi oznaczać dokładnie zero, ale także wartość zbyt małą, aby można ją było przedstawić za pomocą danego typu danych, np.

a = -1 / 1000000000000000000.0

a jest zbyt małe, aby mogło być poprawnie reprezentowane przez zmiennoprzecinkowe (32 bity), więc jest „zaokrąglone” do -0.

Powiedzmy, że nasze obliczenia są kontynuowane:

b = 1 / a

Ponieważ a jest zmiennoprzecinkowe, spowoduje to nieskończoność, która jest daleka od poprawnej odpowiedzi -1000000000000000000.0

Teraz obliczmy b, jeśli nie ma -0 (więc zaokrąglono do +0):

b = 1 / +0
b = +infinity

Wynik jest znowu błędny z powodu zaokrąglenia, ale teraz jest „bardziej zły” - nie tylko liczbowo, ale co ważniejsze z powodu innego znaku (wynikiem obliczenia jest + nieskończoność, poprawny wynik to -1000000000000000000.0).

Nadal można powiedzieć, że to nie ma znaczenia, ponieważ oba są w błędzie. Ważne jest to, że istnieje wiele aplikacji numerycznych, w których najważniejszym wynikiem obliczeń jest znak - np. Przy podejmowaniu decyzji o skręceniu w lewo lub w prawo na skrzyżowaniu za pomocą algorytmu uczenia maszynowego można zinterpretować wartość dodatnią => skręt w lewo, wartość ujemna => skręć w prawo, rzeczywista „wielkość” wartości jest po prostu „współczynnikiem ufności”.

qbd
źródło
Czy masz jakieś pomysły, czy znak niedomiaru może być szczególnie ważny w wyimaginowanych / złożonych liczbach?
jpmc26
@qbd: Czy wiesz, jakie są te aplikacje numeryczne? Powiedziałbym, że programy, które uruchamiają się i używają +inforaz -infpodczas normalnej pracy są uszkodzone.
Björn Lindqvist
@ BjörnLindqvist Jeśli chcesz konkretnych aplikacji do pobrania - nie znam ich. Nie wydaje mi się, żeby to było buggy - zamiast float / double możesz użyć czegoś takiego jak BigDecimal z nieograniczoną precyzją. Ale czy warto, gdy program da dokładnie takie same wyniki jak ten z float / double, ale ze znacznie gorszą wydajnością?
qbd
Napisałeś „aplikacje numeryczne, w których najważniejszym wynikiem obliczeń jest znak”. Mogę w to uwierzyć, ale nie mogę uwierzyć, że istnieją dobrze napisane aplikacje, które opierają się na -0 oraz na wartościach +infi -inf. Jeśli twój program powoduje niedopasowanie zmiennoprzecinkowe, to jest to błąd, a to, co dzieje się później, nie jest tak interesujące, imho. Wciąż brakuje nam praktycznych przykładów, w których przydatna jest wartość -0.
Björn Lindqvist
1
@ BjörnLindqvist Duża część x265 odbywa się w asemblerze, opierając się na niejasnych szczegółach (zależnych od architektury procesora), o których niewiele osób wie w imię wydajności. Czy to źle? Poleganie na szeroko wdrażanym 30-letnim standardzie (który ma pozostać) dla jednej prostej, dobrze zrozumiałej funkcji w imię wydajności nagle nie wydaje się takie złe.
qbd
8

Po pierwsze, jak utworzyć -0? Istnieją dwa sposoby: (1) wykonaj operację zmiennoprzecinkową, w której wynik matematyczny jest ujemny, ale tak blisko zera, że ​​zostaje zaokrąglony do zera, a nie do liczby niezerowej. To obliczenie da -0. (b) Niektóre operacje z zerami: Pomnóż dodatnie zero przez liczbę ujemną lub podziel dodatnie zero przez liczbę ujemną lub neguj dodatnie zero.

Posiadanie ujemnego zera nieco upraszcza mnożenie i dzielenie, znak x * y lub x / y jest zawsze znakiem x, wyłącznym lub znakiem y. Bez ujemnego zera konieczne byłoby dodatkowe sprawdzenie, aby zastąpić -0 +0.

Jest kilka bardzo rzadkich sytuacji, w których jest to przydatne. Możesz sprawdzić, czy wynik pomnożenia lub dzielenia jest matematycznie większy lub mniejszy od zera, nawet jeśli występuje niedopełnienie (o ile wiesz, że wynik nie jest matematycznym zerem). Nie pamiętam, żeby kiedykolwiek napisałem kod, który ma znaczenie.

Optymalizacja kompilatorów nienawidzi -0. Na przykład nie można zastąpić x + 0,0 x, ponieważ wynik nie powinien być x, jeśli x wynosi -0,0. Nie można zamienić x * 0,0 na 0,0, ponieważ wynik powinien wynosić -0,0, jeśli x <0 lub x wynosi -0,0.

gnasher729
źródło
7
Chciałbym, aby IEEE-754 zawierał cztery zera: „dokładny”, dodatni nieskończenie mały, ujemny nieskończenie mały i bez znaku (ta ostatnia stanowi różnicę między wartościami nie do odróżnienia). W ten sposób działałoby wiele aksjomatów zmiennoprzecinkowych - wśród nich x + 0,0 equiv x-0,0 equiv x, xy equiv x + (- 1,0) * yi 1,0 / x equiv -1,0 / (- 1,0 * x) [jeśli x jest dodatnim zerem, oba byłyby pos-inf; jeśli neg-zero, oba neg-inf; jeśli dokładne lub niepodpisane, oba NaN].
supercat
Byłem w stanie uzyskać ujemne zero, przechodząc -5i 5wchodząc fmod(). To dość denerwujące dla mojego przypadku użycia.
Aaron Franke
6

C # Double, który jest zgodny z IEEE 754

    double a = 3.0;
    double b = 0.0;
    double c = -0.0;

    Console.WriteLine(a / b);
    Console.WriteLine(a / c);

drukuje:

Infinity
-Infinity

właściwie to trochę wyjaśnić ...

Double d = -0.0; 

Oznacza to coś znacznie bliższego d = The Limit of x as x approaches 0-lub The Limit of x as x approaches 0 from the negatives.


Aby odpowiedzieć na komentarz Filipa ...

Zasadniczo zero ujemne oznacza niedopełnienie.

Praktyczne zastosowanie ujemnego zera, jeśli w ogóle, jest bardzo mało praktyczne ...

na przykład ten kod (ponownie C #):

double a = -0.0;
double b = 0.0;

Console.WriteLine(a.Equals(b));
Console.WriteLine(a==b);
Console.WriteLine(Math.Sign(a));

daje ten wynik:

True
True
0

Aby wyjaśnić nieformalnie, wszystkie wartości specjalne, które może mieć zmiennoprzecinkowy IEEE 754 (dodatnia nieskończoność, ujemna nieskończoność, NAN, -0,0) nie mają znaczenia w sensie praktycznym. Nie mogą reprezentować żadnej wartości fizycznej ani żadnej wartości, która ma sens w obliczeniach „rzeczywistego świata”. Chodzi im zasadniczo o to:

  • dodatnia nieskończoność oznacza przepełnienie na dodatnim końcu, które może reprezentować zmiennoprzecinkowa
  • ujemna nieskończoność oznacza przepełnienie na dodatnim końcu, które może reprezentować zmiennoprzecinkowa
  • ujemne zero oznacza niedopełnienie, a operandy miały przeciwne znaki
  • dodatnie zero może oznaczać niedopełnienie, a operandy mają ten sam znak
  • NAN oznacza, że ​​twoje obliczenia są całkowicie niezdefiniowane, jak sqrt(-7), lub nie ma limitu podobnego 0/0lub podobnegoPositiveInfinity/PositiveInfinity
AK_
źródło
7
Tak, ale dlaczego to takie ważne? Czy możesz podać praktyczny przykład, w którym różnica ma znaczenie?
Philipp
5

Pytanie o to, w jaki sposób odnosi się to do obliczeń liczb zespolonych, naprawdę stanowi sedno tego, dlaczego zarówno +0, jak i -0 istnieją w zmiennoprzecinkowym. Jeśli w ogóle studiujesz analizę złożoną, szybko odkrywasz, że funkcji ciągłych od złożonych do złożonych zwykle nie można traktować jako „pojedynczej wartości”, chyba że przyjmie się „uprzejmą fikcję”, że dane wyjściowe tworzą tak zwaną „powierzchnię Riemanna”. Na przykład logarytm złożony przypisuje każdemu wejściowi nieskończenie wiele wyników; kiedy „łączysz je” w celu uzyskania ciągłego wyniku, wszystkie części rzeczywiste tworzą „nieskończoną powierzchnię korkociągu” wokół źródła. Ciągła krzywa, która przecina rzeczywistą oś „w dół od strony urojonej pozytywnie” i kolejna krzywa, która „owija się wokół bieguna” i przecina rzeczywistą oś ”

Teraz zastosuj to do programu numerycznego, który oblicza za pomocą złożonego zmiennoprzecinkowego. Działanie podjęte po danym obliczeniu może się bardzo różnić w zależności od tego, który „arkusz” jest obecnie „włączony”, a znak ostatniego obliczonego wyniku prawdopodobnie mówi, który „arkusz”. Załóżmy teraz, że wynik był zerowy? Pamiętaj, że „zero” naprawdę oznacza „zbyt małe, aby poprawnie reprezentować”. Ale jeśli obliczenia mogłyby zapewnić -zachowanie znaku- (tj. Zapamiętanie, który „arkusz”), gdy wynik wynosi zero, kod może sprawdzić znak i wykonać właściwą akcję nawet w tej sytuacji.

PJM
źródło
1

Powód jest prostszy niż zwykle

Oczywiście jest wiele hacków, które wyglądają naprawdę ładnie i są użyteczne (jak zaokrąglanie do -0.0lub, +0.0ale załóżmy, że mamy reprezentację podpisanej int ze znakiem minus / plus na początku (wiem, że jest to rozwiązane przez kod binarny U2) w liczbach całkowitych zwykle, ale przyjmują mniej złożoną reprezentację podwójnej):

0 111 = 7
^ sign

Co jeśli jest liczba ujemna?

1 111 = -7

Okej, to proste. Więc reprezentujemy 0:

0 000 = 0

To też w porządku. Ale co z tym 1 000? Czy to musi być zakazany numer? Lepiej nie.

Załóżmy więc, że istnieją dwa typy zera:

0 000 = +0
1 000 = -0

To uprości nasze obliczenia i na szczęście zapewni dodatkowe zaokrąglenie. Więc +0i -0pochodzą tylko z kwestii reprezentacji binarnej.

Dawid Pura
źródło
6
Jeśli czytam to poprawnie, zasadniczo mówisz po prostu, że ludzie definiujący lub wdrażający standardy nie chcieli zadawać sobie trudu, aby to zabronić. Nie sądzę, aby to rozumowanie utrzymywało fakt, że dopełniacz 2 używa reprezentacji „ujemnego zera” dla zupełnie innej liczby i nie ma reprezentacji ujemnego zera. Zobacz artykuł w Wikipedii, który podłączyłem.
jpmc26
1
@ jpmc26 Myślę, że tak naprawdę jest w tym trochę prawdy, że nie zakazywanie oznacza, że ​​nie trzeba wymagać od implementacji specjalnego przypadku. W tej chwili każda liczba ma bit znaku i można go zanegować, przełączając bit znaku. Nawet NaN są podpisane, a implementacje mogą (ale nie są wymagane) wybierać odpowiedni znak podczas tworzenia NaN. Jeśli ujemne zero nie istniałoby, każde obliczenie, które skutkowało 0, wymagałoby dodatkowej pracy, aby naprawić bit znaku itp.
hobbs
4
@ jpmc26 (tj. przy każdym innym pomnożeniu dwóch liczb, znakiem wyniku jest xor znaku wielokrotności, a wielkość jest iloczynem dwóch wielkości. W rzeczywistości działa to dla -1 * 0 = - 0. Ale jeśli zero z odwróconym bitem znaku było jakąś specjalną niezerową wartością, każdy produkt, który mógłby wytworzyć 0, musiałby sprawdzić i upewnić się, że nie wygeneruje tej specjalnej wartości przez pomyłkę.)
hobbs