Por que Math.pow (0, 0) === 1?

85

Todos nós sabemos que 0 0 é indeterminado.

Mas , javascript diz que:

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

e C ++ diz a mesma coisa:

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

PORQUE?

Eu sei disso:

>Math.pow(0.001, 0.001)
0.9931160484209338

Mas por que não Math.pow(0, 0)lança nenhum erro? Ou talvez NaNseja melhor do que 1.

Ionică Bizău
fonte
3
@zzzzBov: Na definição padrão, "a <sup> b </sup> = exp (b ln (a))", é indefinido. Tentar defini-lo como "limite <sub> x-> 0 </sub> f (x) <sup> g (x) </sup>" onde "f" e "g" têm limites de zero resulta em um indeterminado valor, uma vez que depende da sua escolha de funções. (Desculpas pela notação distorcida; não consigo descobrir como obter sobrescritos nos comentários).
Mike Seymour
@MikeSeymour, sim, estou ciente de que 0⁰ (usar caracteres Unicode) é indefinido dada essa definição, no entanto, se você leu meu comentário, deve notar que a citação faz referência ao "mundo da matemática" ao invés de qualquer "definição padrão". É esta diferença que eu estava me referindo originalmente, e a pergunta foi atualizada para corrigir essa nuance.
zzzzBov
2
@AJMansfield Um ... a ^ 0 = 1 para diferente de zero a.
Beska
Ele permite que funções que dependem de produtos de probabilidades entreguem resultados razoáveis. É uma noção incorreta de que os computadores são processadores matemáticos simbólicos. A linguagem C tem uma implementação específica no mundo real, enquanto seu mundo matemático pode ser muito ideal para ser implementado em silício.
IRTFM
26
Para a versão matemática desta pergunta - "por que costumamos definir 0 ^ 0 = 1?" - math.stackexchange tem muitas respostas boas: math.stackexchange.com/questions/11150/…
PLL

Respostas:

78

Em C ++ O resultado de pow (0, 0) , o resultado é basicamente implementação comportamento definido desde matematicamente temos uma situação contraditória em que N^0deve ser sempre 1, mas 0^Ndeve ser sempre 0para N > 0, então você não deve ter expectativas matematicamente quanto ao resultado deste quer. Esta postagem do fórum do Wolfram Alpha dá mais detalhes.

Embora ter pow(0,0)resultado em 1seja útil para muitas aplicações, como a justificativa para o padrão internacional - linguagens de programação - C afirma na seção que cobre o suporte aritmético de ponto flutuante IEC 60559 :

Geralmente, C99 evita um resultado NaN onde um valor numérico é útil. [...] Os resultados de pow (∞, 0) e pow (0,0) são ambos 1, porque existem aplicativos que podem explorar esta definição. Por exemplo, se x (p) e y (p) são quaisquer funções analíticas que se tornam zero em p = a, então pow (x, y), que é igual a exp (y * log (x)), se aproxima de 1 conforme p se aproxima uma.

Atualizar C ++

Como leemes corretamente apontou, eu originalmente vinculei à referência para a versão complexa de pow enquanto a versão não complexa afirma que é um erro de domínio, o esboço do padrão C ++ volta ao esboço do padrão C e ambos C99 e C11 na seção 7.12.7.4 O parágrafo das funções pow 2 diz ( ênfase minha ):

[...] Um erro de domínio Pode ocorrer se x for zero ey for zero. [...]

que, tanto quanto eu posso dizer meio deste comportamento é o comportamento não especificado liquidação de volta uma seção pouco7.12.1 Tratamento de condições de erro diz:

[...] um erro de domínio ocorre se um argumento de entrada está fora do domínio sobre o qual a função matemática é definida. [...] Em um erro de domínio, a função retorna um valor definido pela implementação; se a expressão inteira math_errhandling & MATH_ERRNO for diferente de zero, a expressão inteira errno adquire o valor EDOM; [...]

Portanto, se houvesse um erro de domínio , esse seria o comportamento definido pela implementação, mas em ambas as versões mais recentes de gcce clango valor de errnoé, 0portanto, não é um erro de domínio para esses compiladores.

Atualizar Javascript

Para Javascript, a Especificação da Linguagem ECMAScript® na seção 15.8 O Objeto Matemático sob 15.8.2.13 pow (x, y) diz, entre outras condições:

Se y for +0, o resultado será 1, mesmo se x for NaN.

Shafik Yaghmour
fonte
1
@leemes acredito que a pagina esta errada, a norma nao diz que NaN deva ser retornado. O valor de retorno é definido pela implementação. cplusplus.com, que você afirma não ser uma fonte confiável, é realmente mais preciso aqui.
intervalo de
@interjay Acho que você quer dizer a resposta excluída; Eu apenas citei sobre sua falta de confiabilidade, esperando que pudesse explicar o downvote (que não foi por mim). Bem, ambas as páginas são wikis, então sua confiabilidade depende de seus editores, que são humanos e cometem erros. ;)
leemes
@leemes a comunidade C ++ no SO definitivamente não gosta do cplusplus.com
Shafik Yaghmour
@ShafikYaghmour Vinculei a mesma pergunta (na resposta excluída).
leemes
1
@Alek Agradeço o feedback, tento escrever as respostas que gostaria de ler de outras pessoas. Nem sempre consigo, mas tento. Escrever boas perguntas é ainda mais difícil, eu só tentei isso uma vez e gastei muito mais tempo nisso do que nas minhas respostas.
Shafik Yaghmour
35

Em JavaScript Math.powé definido da seguinte forma :

  • Se y for NaN, o resultado será NaN.
  • Se y for +0, o resultado será 1, mesmo se x for NaN.
  • Se y for −0, o resultado será 1, mesmo se x for NaN.
  • Se x for NaN e y for diferente de zero, o resultado será NaN.
  • Se abs (x)> 1 ey for + ∞, o resultado será + ∞.
  • Se abs (x)> 1 ey for −∞, o resultado será +0.
  • Se abs (x) == 1 ey for + ∞, o resultado será NaN.
  • Se abs (x) == 1 ey for −∞, o resultado é NaN.
  • Se abs (x) <1 ey for + ∞, o resultado será +0.
  • Se abs (x) <1 e y for −∞, o resultado será + ∞.
  • Se x for + ∞ e y> 0, o resultado será + ∞.
  • Se x for + ∞ e y <0, o resultado será +0.
  • Se x for −∞ ey> 0 ey for um número inteiro ímpar, o resultado será −∞.
  • Se x for −∞ ey> 0 ey não for um número inteiro ímpar, o resultado será + ∞.
  • Se x for −∞ ey <0 e y for um número inteiro ímpar, o resultado será −0.
  • Se x for −∞ ey <0 e y não for um número inteiro ímpar, o resultado será +0.
  • Se x for +0 ey> 0, o resultado será +0.
  • Se x for +0 ey <0, o resultado será + ∞.
  • Se x for −0 ey> 0 ey for um número inteiro ímpar, o resultado será −0.
  • Se x for −0 ey> 0 ey não for um número inteiro ímpar, o resultado será +0.
  • Se x for −0 ey <0 ey for um número inteiro ímpar, o resultado será −∞.
  • Se x for −0 ey <0 e y não for um número inteiro ímpar, o resultado será + ∞.
  • Se x <0 e x for finito ey for finito ey não for um inteiro, o resultado será NaN.

ênfase minha

como regra geral, as funções nativas de qualquer idioma devem funcionar conforme descrito na especificação do idioma. Às vezes, isso inclui explicitamente "comportamento indefinido", em que cabe ao implementador determinar qual deve ser o resultado; no entanto, esse não é um caso de comportamento indefinido.

zzzzBov
fonte
O Anexo F nas normas C99 e C11 contém esta mesma especificação. Uma implementação deve definir __STDC_IEC_559__para anunciar que está em conformidade com esta especificação. O anexo F descreve a aritmética de ponto flutuante IEC 60559. Eu acredito que uma especificação C pode estar parcialmente em conformidade com o Anexo F (por exemplo, pow (0, 0) == 1) e não definir __STDC_IEC_559__.
Howard Hinnant
@HowardHinnant hmmm, parece que no caso do gcc e do clang essa informação pode não ser totalmente útil, isso é desanimador.
Shafik Yaghmour
6
Não sei se essa resposta ajuda. Obviamente, a função deve funcionar conforme definido nas especificações. Mas então a questão se torna "Por que foi definido dessa forma na especificação?"
Beska
Ainda bem que isso é (provavelmente) feito em hardware, caso contrário, prejudicaria o desempenho com todos esses casos especiais :)
Thomas
16

É apenas uma convenção para defini-lo como 1, 0ou para deixá-lo undefined. A definição pow (0,0)é ampla devido à seguinte definição:

definição matemática de poder


A documentação do ECMA-Script diz o seguinte sobre pow(x,y):

  • Se y for +0, o resultado será 1, mesmo se x for NaN.
  • Se y for −0, o resultado será 1, mesmo se x for NaN.

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

Schmijos
fonte
3
math.stackexchange tem muitas boas discussões e explicações para a definição 0 ^ 0 = 1: math.stackexchange.com/questions/11150/…
PLL
14

De acordo com a Wikipedia:

Na maioria das configurações que não envolvem continuidade no expoente, interpretar 0 0 como 1 simplifica as fórmulas e elimina a necessidade de casos especiais em teoremas.

Existem várias maneiras possíveis de tratar os 0**0prós e os contras de cada um (consulte a Wikipedia para uma discussão extensa).

O padrão de ponto flutuante IEEE 754-2008 recomenda três funções diferentes:

  • powtrata 0**0como 1. Esta é a versão mais antiga definida. Se a potência for um inteiro exato, o resultado é o mesmo que para pown, caso contrário, o resultado é igual a powr(exceto em alguns casos excepcionais).
  • powntrata 0 ** 0 como 1. A potência deve ser um número inteiro exato. O valor é definido para bases negativas; por exemplo, pown(−3,5)é −243.
  • powrtrata 0 ** 0 como NaN (não é um número - indefinido). O valor também é NaN para casos em powr(−3,2)que a base é menor que zero. O valor é definido por exp (potência '× log (base)).
NPE
fonte
6

Donald Knuth

meio que resolveu esse debate em 1992 com o seguinte:

insira a descrição da imagem aqui

E entrou ainda mais em detalhes em seu artigo Two Notes on Notation .

Basicamente, embora não tenhamos 1 como o limite de f(x)/g(x)para todas nem todas as funções f(x)e g(x), ainda torna a combinatória muito mais simples de definir 0^0=1e, em seguida, apenas cria casos especiais nos poucos lugares onde você precisa considerar funções como 0^x, que são estranhos de qualquer maneira. Afinal, x^0surge com muito mais frequência.

Algumas das melhores discussões que conheço sobre este tópico (além do artigo de Knuth) são:

Thomas Ahle
fonte
Se você ainda não leu algum, leia as respostas do Zero à potência zero ...? que estava ligada à pergunta você deveria, algumas das respostas abrangem essa abordagem também.
Shafik Yaghmour
5

Quando você quer saber a qual valor deve dar f(a)quando fnão é diretamente computável em a, você calcula o limite de fquando xtende para a.

No caso de x^y, os limites usuais tendem para 1quando xe ytendem para 0, e especialmente x^xtendem para 1quando xtendem para 0.

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

Denys Séguret
fonte
5

A definição da linguagem C diz (7.12.7.4/2):

Um erro de domínio pode ocorrer se x for zero ey for zero.

Também diz (7.12.1 / 2):

Em um erro de domínio, a função retorna um valor definido pela implementação; se a expressão inteira math_errhandling & MATH_ERRNO for diferente de zero, a expressão inteira errno adquire o valor EDOM; se a expressão inteira math_errhandling & MATH_ERREXCEPT for diferente de zero, a exceção de ponto flutuante '' inválido '' é levantada.

Por padrão, o valor de math_errhandlingé MATH_ERRNO, portanto, verifique errnoo valor EDOM.

Pete Becker
fonte
1
Whoups! Isso é muito interessante! g++ (Ubuntu/Linaro 4.8.1-10ubuntu8) 4.8.
Compilei
0

Eu gostaria de discordar da afirmação de algumas das respostas anteriores de que é uma questão de convenção ou conveniência (cobrindo alguns casos especiais para vários teoremas, etc.) que 0 ^ 0 seja definido como 1 em vez de 0.

A exponenciação não se encaixa muito bem com nossas outras notações matemáticas, então a definição que todos aprendemos deixa espaço para confusão. Uma maneira ligeiramente diferente de abordar isso é dizer que a ^ b (ou exp (a, b), se quiser) retorna o valor multiplicativamente equivalente a multiplicar alguma outra coisa por a, repetido b vezes.

Quando multiplicamos 5 por 4, 2 vezes, obtemos 80. Multiplicamos 5 por 16. Portanto, 4 ^ 2 = 16.

Quando você multiplica 14 por 0, 0 vezes, ficamos com 14. Nós multiplicamos 1. Portanto, 0 ^ 0 = 1.

Essa linha de pensamento também pode ajudar a esclarecer os expoentes negativos e fracionários. 4 ^ (- 2) é um 16º, porque 'multiplicação negativa' é divisão - dividimos por quatro duas vezes.

a ^ (1/2) é root (a), porque multiplicar algo pela raiz de a é metade do trabalho multiplicativo como multiplicar por a ele mesmo - você teria que fazer isso duas vezes para multiplicar algo por 4 = 4 ^ 1 = (4 ^ (1/2)) ^ 2

NiloCK
fonte
0

Para que isso entenda, você precisa resolver o cálculo:

insira a descrição da imagem aqui

Expandindo em x^xtorno de zero usando a série Taylor, obtemos:

insira a descrição da imagem aqui

Então, para entender o que está acontecendo com o limite quando xvai para zero, precisamos descobrir o que está acontecendo com o segundo mandato x log(x), porque outros termos são proporcionais a x log(x)elevados a alguma potência.

Precisamos usar a transformação:

insira a descrição da imagem aqui

Agora, após esta transformação, podemos usar a regra de L'Hôpital , que afirma que:

insira a descrição da imagem aqui

Assim, diferenciando essa transformação, obtemos:

insira a descrição da imagem aqui

Portanto, calculamos que o termo se log(x)*xaproxima de 0 quando x se aproxima de 0. É fácil ver que outros termos consecutivos também se aproximam de zero e ainda mais rápido do que o segundo termo.

Então, nesse ponto x=0, a série se torna 1 + 0 + 0 + 0 + ...e, portanto, é igual a 1.

Agnius Vasiliauskas
fonte
Embora essa resposta seja impressionante, é importante notar que em matemática, o limite como x-> a de f (x) não é necessariamente igual a f (a), a menos que a função seja contínua em x.
jasonszhao