Dlaczego Math.pow (0, 0) === 1?

85

Wszyscy wiemy, że 0 0 jest nieokreślone.

Ale , javascript mówi, że:

Math.pow(0, 0) === 1 // true

a C ++ mówi to samo:

pow(0, 0) == 1 // true

CZEMU?

Wiem to:

>Math.pow(0.001, 0.001)
0.9931160484209338

Ale dlaczego nie Math.pow(0, 0)rzuca żadnych błędów? A może NaNbyłoby lepsze niż 1.

Ionică Bizău
źródło
3
@zzzzBov: Zgodnie ze standardową definicją „a <sup> b </sup> = exp (b ln (a))” jest niezdefiniowane. Próba zdefiniowania tego jako „limit <sub> x-> 0 </sub> f (x) <sup> g (x) </sup>”, gdzie „f” i „g” mają granice zerowe, daje wynik nieokreślony wartość, ponieważ zależy to od wyboru funkcji. (Przepraszamy za zniekształconą notację; nie mogę dowiedzieć się, jak uzyskać indeksy górne w komentarzach).
Mike Seymour
@MikeSeymour, tak, zdaję sobie sprawę, że 0⁰ (użyj znaków Unicode) jest niezdefiniowane, biorąc pod uwagę tę definicję, jednak jeśli czytasz mój komentarz, powinieneś zauważyć, że cytat odnosi się do „świata matematyki”, a nie do jakiejkolwiek „standardowej definicji”. To ta różnica, o której pierwotnie mówiłem, a pytanie zostało zaktualizowane, aby poprawić ten niuans.
zzzzBov
2
@AJMansfield Um ... a ^ 0 = 1 dla niezerowej a.
Beska
Umożliwia funkcjom zależnym od iloczynów prawdopodobieństwa dostarczanie sensownych wyników. To błędne przekonanie, że komputery są symbolicznymi procesorami matematycznymi. Język C ma specyficzną implementację w prawdziwym świecie, podczas gdy twój matematyczny świat może być zbyt idealny, aby go wprowadzić w krzem.
IRTFM
26
W matematycznej wersji tego pytania - „dlaczego często definiujemy 0 ^ 0 = 1?” - math.stackexchange ma wiele dobrych odpowiedzi: math.stackexchange.com/questions/11150/ ...
PLL

Odpowiedzi:

78

W C ++ wynikiem pow (0, 0), wynik jest w zasadzie realizacja zdefiniowane zachowanie ponieważ matematycznie mamy sprzeczne sytuacji, w której N^0powinien być zawsze 1, ale 0^Nzawsze powinien być 0za N > 0, więc nie powinno być żadnych oczekiwań matematycznie co do wyniku tego obaj. Ten post na forum Wolfram Alpha zawiera nieco więcej szczegółów.

Chociaż uzyskanie pow(0,0)wyniku 1jest przydatne w wielu aplikacjach, ponieważ uzasadnienie dla normy międzynarodowej - języki programowania - stwierdza C w sekcji dotyczącej obsługi arytmetyki zmiennoprzecinkowej normy IEC 60559 :

Ogólnie C99 unika wyniku NaN, gdy przydatna jest wartość liczbowa. [...] Wyniki pow (∞, 0) i pow (0,0) wynoszą 1, ponieważ istnieją aplikacje, które mogą wykorzystywać tę definicję. Na przykład, jeśli x (p) i y (p) są dowolnymi funkcjami analitycznymi, które stają się zerowe przy p = a, to pow (x, y), które jest równe exp (y * log (x)), zbliża się do 1, gdy p zbliża się za.

Zaktualizuj C ++

Jak leemes prawidłowo wskazał, że pierwotnie związany z odniesieniem do złożonej wersji pow natomiast zakaz złożonych roszczeń wersji jest to błąd domeny projekt C średnia ++ wraca do normy projekt C i zarówno C99 i C11 w sekcji 7.12.7.4 Funkcje pow ustęp 2 mówi ( podkreślenie moje ):

[...] Błąd domeny może wystąpić, jeśli x wynosi zero, a y wynosi zero. [...]

co, o ile wiem, oznacza, że ​​to zachowanie jest zachowaniem nieokreślonym. Wycofanie sekcji nieco w sekcji 7.12.1 Traktowanie warunków błędu mówi:

[...] błąd domeny występuje, jeśli argument wejściowy znajduje się poza dziedziną, w której zdefiniowano funkcję matematyczną. [...] W przypadku błędu domeny funkcja zwraca wartość zdefiniowaną w ramach implementacji; jeśli wyrażenie całkowite math_errhandling & MATH_ERRNO jest niezerowe, wyrażenie całkowite errno uzyskuje wartość EDOM; […]

Jeśli więc wystąpił błąd domeny, byłoby to zachowanie zdefiniowane w ramach implementacji, ale zarówno w najnowszych wersjach, jak gcci clangwartość errnojest 0taka, więc nie jest to błąd domeny dla tych kompilatorów.

Zaktualizuj Javascript

Dla JavaScript ECMAScript® Language Specification w punkcie 15.8 obiektu Math pod 15.8.2.13 pow (x, y) , mówi między innymi warunkami, że:

Jeśli y wynosi +0, wynikiem jest 1, nawet jeśli x wynosi NaN.

Shafik Yaghmour
źródło
1
@leemes Uważam, że strona jest nieprawidłowa, standard nie mówi, że należy zwrócić NaN. Wartość zwracana jest zdefiniowana w ramach implementacji. cplusplus.com, który Twoim zdaniem nie jest wiarygodnym źródłem, jest tutaj dokładniejszy.
interjay
@interjay Chyba masz na myśli usuniętą odpowiedź; Zacytowałem tylko jego niewiarygodność, mając nadzieję, że może to wyjaśnić negatywny głos (który nie był przeze mnie). Cóż, obie strony to wiki, więc ich niezawodność zależy od ich redaktorów, którzy są ludźmi i popełniają błędy. ;)
leemes
@leemes społeczność C ++ na SO zdecydowanie ma silną niechęć do cplusplus.com
Shafik Yaghmour
@ShafikYaghmour Połączyłem to samo pytanie (w usuniętej odpowiedzi).
leemes
1
@Alek Doceniam opinie, staram się pisać odpowiedzi, które chciałbym przeczytać od innych. Nie zawsze mi się to udaje, ale próbuję. Pisanie dobrych pytań jest jeszcze trudniejsze, próbowałem tego tylko raz i spędziłem nad tym znacznie dłużej niż na odpowiedziach.
Shafik Yaghmour
35

W JavaScript Math.powdefiniuje się następująco :

  • Jeśli y jest NaN, wynikiem jest NaN.
  • Jeśli y wynosi +0, wynikiem jest 1, nawet jeśli x wynosi NaN.
  • Jeśli y wynosi −0, wynikiem jest 1, nawet jeśli x wynosi NaN.
  • Jeśli x jest NaN, a y jest niezerowe, wynikiem jest NaN.
  • Jeśli abs (x)> 1, a y wynosi + ∞, wynikiem jest + ∞.
  • Jeśli abs (x)> 1, a y wynosi −∞, wynikiem jest +0.
  • Jeśli abs (x) == 1, a y to + ∞, wynikiem jest NaN.
  • Jeśli abs (x) == 1, a y wynosi −∞, wynikiem jest NaN.
  • Jeśli abs (x) <1, a y wynosi + ∞, wynikiem jest +0.
  • Jeśli abs (x) <1, a y wynosi −∞, wynikiem jest + ∞.
  • Jeśli x wynosi + ∞, a y> 0, wynikiem jest + ∞.
  • Jeśli x wynosi + ∞, a y <0, wynikiem jest +0.
  • Jeśli x wynosi −∞, a y> 0, a y jest nieparzystą liczbą całkowitą, wynikiem jest −∞.
  • Jeśli x wynosi −∞, a y> 0, a y nie jest nieparzystą liczbą całkowitą, wynikiem jest + ∞.
  • Jeśli x wynosi −∞, a y <0, a y jest nieparzystą liczbą całkowitą, wynikiem jest −0.
  • Jeśli x wynosi −∞, a y <0, a y nie jest nieparzystą liczbą całkowitą, wynikiem jest +0.
  • Jeśli x wynosi +0, a y> 0, wynikiem jest +0.
  • Jeśli x wynosi +0, a y <0, wynikiem jest + ∞.
  • Jeśli x wynosi −0, a y> 0, a y jest nieparzystą liczbą całkowitą, wynikiem jest −0.
  • Jeśli x wynosi −0, a y> 0, a y nie jest nieparzystą liczbą całkowitą, wynikiem jest +0.
  • Jeśli x wynosi −0, a y <0, a y jest nieparzystą liczbą całkowitą, wynikiem jest −∞.
  • Jeśli x wynosi −0, a y <0, a y nie jest nieparzystą liczbą całkowitą, wynikiem jest + ∞.
  • Jeśli x <0, a x jest skończone, a y jest skończone, a y nie jest liczbą całkowitą, wynikiem jest NaN.

podkreślenie moje

co do zasady, funkcje natywne dla dowolnego języka powinny działać zgodnie z opisem w specyfikacji języka. Czasami obejmuje to jawnie „niezdefiniowane zachowanie”, w którym implementacja określa, jaki powinien być wynik, jednak nie jest to przypadek niezdefiniowanego zachowania.

zzzzBov
źródło
Załącznik F do norm C99 i C11 zawiera tę samą specyfikację. Implementacja ma zdefiniować, __STDC_IEC_559__aby ogłosić, że jest zgodna z tą specyfikacją. Załącznik F opisuje arytmetykę zmiennoprzecinkową normy IEC 60559. Uważam, że specyfikacja C może być częściowo zgodna z załącznikiem F (np. Pow (0, 0) == 1), a nie zdefiniowana __STDC_IEC_559__.
Howard Hinnant
@HowardHinnant hmmm, wygląda na to, że w przypadku gcc i clang ta informacja może nie być do końca pomocna, czyli zniechęcająca.
Shafik Yaghmour
6
Nie wiem, czy ta odpowiedź pomaga. Oczywiście funkcja powinna działać zgodnie z definicją w specyfikacji. Ale wtedy pytanie brzmi: „Dlaczego zostało to zdefiniowane w ten sposób w specyfikacji?”
Beska
Dobrze, że jest to (prawdopodobnie) zrobione na sprzęcie, w przeciwnym razie zniweczyłoby wydajność we wszystkich tych specjalnych przypadkach :)
Thomas
16

To jest po prostu konwencja go zdefiniować jako 1, 0lub go opuścić undefined. Definicja pow (0,0)jest szeroko rozpowszechniona ze względu na następującą definicję:

matematyczna definicja mocy


Dokumentacja ECMA-Script mówi co następuje pow(x,y):

  • Jeśli y wynosi +0, wynikiem jest 1, nawet jeśli x wynosi NaN.
  • Jeśli y wynosi −0, wynikiem jest 1, nawet jeśli x wynosi NaN.

[ http://www.ecma-international.org/ecma-262/5.1/#sec-15.8.2.13 ]

schmijos
źródło
3
math.stackexchange zawiera wiele dobrych dyskusji i wyjaśnień na temat definicji 0 ^ 0 = 1: math.stackexchange.com/questions/11150/ ...
PLL
14

Według Wikipedii:

W większości ustawień, które nie obejmują ciągłości wykładnika, interpretacja 0 0 jako 1 upraszcza wzory i eliminuje potrzebę stosowania specjalnych przypadków w twierdzeniach.

Istnieje kilka możliwych sposobów traktowania 0**0za i przeciw dla każdego z nich ( poszerzona dyskusja znajduje się w Wikipedii ).

IEEE 754-2008 pływających standardowy punkt zaleca trzy różne funkcje:

  • powtraktuje 0**0jako 1. To jest najstarsza zdefiniowana wersja. Jeśli potęga jest dokładną liczbą całkowitą, wynik jest taki sam jak for pown, w przeciwnym razie wynik jest jak for powr(z wyjątkiem niektórych wyjątkowych przypadków).
  • powntraktuje 0 ** 0 jako 1. Potęga musi być dokładną liczbą całkowitą. Wartość jest określona dla zasad ujemnych; np . pown(−3,5)jest −243.
  • powrtraktuje 0 ** 0 jako NaN (Not-a-Number - undefined). Wartość wynosi również NaN w przypadkach, powr(−3,2)gdy podstawa jest mniejsza od zera. Wartość jest określona przez exp (potęga × log (podstawa)).
NPE
źródło
6

Donald Knuth

w pewnym sensie rozstrzygnął tę debatę w 1992 r., co następuje:

wprowadź opis obrazu tutaj

Jeszcze więcej szczegółów przedstawił w swoim artykule Two Notes on Notation .

Zasadniczo, chociaż nie mamy 1 jako limitu f(x)/g(x)dla wszystkich nie wszystkich funkcji f(x)i g(x)nadal sprawia, że ​​kombinatoryka jest o wiele prostsza do zdefiniowania 0^0=1, a następnie po prostu utwórz specjalne przypadki w kilku miejscach, w których należy wziąć pod uwagę funkcje, takie jak 0^x, i tak są dziwne. Przecież x^0pojawia się dużo częściej.

Niektóre z najlepszych dyskusji, jakie znam na ten temat (poza artykułem Knutha), to:

Thomas Ahle
źródło
Jeśli jeszcze nie czytałeś, przeczytaj odpowiedzi w Zero do zerowej potęgi ...? które było powiązane z pytaniem, które powinieneś rozważyć, niektóre odpowiedzi obejmują również to podejście.
Shafik Yaghmour
5

Jeśli chcesz wiedzieć, jaką wartość należy nadać, f(a)gdy fnie jest bezpośrednio obliczalna w programie a, obliczasz granicę, fkiedy xzmierza w kierunkua .

W przypadku x^y, zwykłe granice zmierzają do 1kiedy xi ymają tendencję 0, a zwłaszcza x^xdo 1kiedy xmają tendencję 0.

Zobacz http://www.math.hmc.edu/funfacts/ffiles/10005.3-5.shtml

Denys Séguret
źródło
5

Definicja języka C mówi (7.12.7.4/2):

Błąd domeny może wystąpić, jeśli x wynosi zero, a y wynosi zero.

Mówi również (7.12.1 / 2):

W przypadku błędu domeny funkcja zwraca wartość zdefiniowaną w ramach implementacji; jeśli wyrażenie całkowite math_errhandling & MATH_ERRNO jest niezerowe, wyrażenie całkowite errno uzyskuje wartość EDOM; jeśli wyrażenie całkowite math_errhandling & MATH_ERREXCEPT ma wartość różną od zera, zgłaszany jest wyjątek zmiennoprzecinkowy „nieprawidłowy”.

Domyślnie wartością math_errhandlingjest MATH_ERRNO, więc sprawdź errnowartość EDOM.

Pete Becker
źródło
1
Whoups! To naprawdę interesujące! Skompilowałem mój plik cpp za pomocąg++ (Ubuntu/Linaro 4.8.1-10ubuntu8) 4.8.
Ionică Bizău
0

Chciałbym nie zgodzić się z twierdzeniem niektórych z poprzednich odpowiedzi, że jest to kwestia konwencji lub wygody (obejmującej pewne szczególne przypadki dla różnych twierdzeń itp.), Że 0 ^ 0 należy zdefiniować jako 1 zamiast 0.

Potęgowanie nie pasuje tak dobrze do naszych innych notacji matematycznych, więc definicja, której wszyscy się uczymy, pozostawia miejsce na nieporozumienia. Nieco innym sposobem podejścia jest stwierdzenie, że a ^ b (lub exp (a, b), jeśli wolisz) zwraca wartość będącą zwielokrotnieniem równoważnej pomnożeniu jakiejś innej rzeczy przez a, powtórzone b razy.

Kiedy pomnożymy 5 przez 4, 2 razy, otrzymamy 80. Pomnożymy 5 przez 16. Zatem 4 ^ 2 = 16.

Kiedy pomnożymy 14 przez 0, 0 razy, zostaje nam 14. Pomnożymy to 1. Stąd 0 ^ 0 = 1.

Ten sposób myślenia może również pomóc w wyjaśnieniu ujemnych i ułamkowych wykładników. 4 ^ (- 2) to 16., ponieważ „mnożenie ujemne” to dzielenie - dzielimy przez cztery dwukrotnie.

a ^ (1/2) to pierwiastek (a), ponieważ pomnożenie czegoś przez pierwiastek a jest połową pracy mnożenia jako pomnożenie tego przez samo a - musiałbyś to zrobić dwukrotnie, aby pomnożyć coś przez 4 = 4 ^ 1 = (4 ^ (1/2)) ^ 2

NiloCK
źródło
0

Aby to zrozumieć, musisz rozwiązać rachunek różniczkowy:

wprowadź opis obrazu tutaj

Rozwijając x^xwokół zera za pomocą szeregu Taylora, otrzymujemy:

wprowadź opis obrazu tutaj

Aby zrozumieć, co dzieje się z limitem, gdy xdochodzi do zera, musimy dowiedzieć się, co się dzieje z drugim członem x log(x), ponieważ inne terminy są proporcjonalne dox log(x) podniesienia do pewnej potęgi.

Musimy użyć transformacji:

wprowadź opis obrazu tutaj

Teraz po tej transformacji możemy użyć reguły L'Hôpitala , która mówi, że:

wprowadź opis obrazu tutaj

Tak więc różnicując tę ​​transformację otrzymujemy:

wprowadź opis obrazu tutaj

Więc obliczyliśmy ten termin log(x)*x zbliża się do 0, gdy x zbliża się do 0. Łatwo zauważyć, że inne kolejne wyrazy również zbliżają się do zera, a nawet szybciej niż drugi człon.

W pewnym momencie x=0szereg staje się, 1 + 0 + 0 + 0 + ...a zatem równy 1.

Agnius Vasiliauskas
źródło
Chociaż ta odpowiedź jest imponująca, warto zauważyć, że w matematyce granica jako x-> a z f (x) niekoniecznie musi być równa f (a), chyba że funkcja jest ciągła przy x.
jasonszhao