Por que o zero negativo é importante?

64

Estou confuso sobre por que nos preocupamos com diferentes representações para zero positivo e negativo.

Lembro-me vagamente das alegações de leitura de que ter uma representação zero negativa é extremamente importante na programação que envolve números complexos. Eu nunca tive a oportunidade de escrever código que envolva números complexos, por isso estou um pouco confuso sobre por que esse seria o caso.

O artigo da Wikipedia sobre o conceito não é especialmente útil; só faz afirmações vagas sobre zero assinado, tornando certas operações matemáticas mais simples em ponto flutuante, se bem entendi. Esta resposta lista algumas funções que se comportam de maneira diferente, e talvez algo possa ser inferido nos exemplos se você estiver familiarizado com o modo como elas podem ser usadas. (Embora o exemplo particular das raízes quadradas complexas pareça totalmente errado, já que os dois números são matematicamente equivalentes, a menos que eu tenha um mal-entendido.) Mas não consegui encontrar uma declaração clara do tipo de problema que você enfrentaria se não estivesse lá. Os recursos mais matemáticos que pude encontrar afirmam que não há distinção entre os dois de uma perspectiva matemática, e o artigo da Wikipedia parece sugerir que isso raramente é visto fora da computação, além de descrever limites.

Então, por que um zero negativo é valioso na computação? Tenho certeza de que estou perdendo alguma coisa.

jpmc26
fonte
6
O zero negativo pode sinalizar subfluxo em um número de ponto flutuante IEEE, mas além disso, seu uso parece controverso e obscuro. Se eu fosse adivinhar, diria que o zero negativo é representado no ponto flutuante IEEE porque ... bem, você pode. Para um passeio ainda mais interessante, procure as informações sobre o ponto flutuante sinalizando NaN.
Robert Harvey
11
Se o exemplo em particular for "1 / 0,0" / "1 / -0,0", 0 será um corte de ramificação para 1 / xo limite dependerá se você o abordar de baixo ou de cima.
Vatine
@Vatine Não, o exemplo em particular é sqrt(-1+0i) = ie sqrt(-1-0i) = -i, embora esteja vestido com a sintaxe apropriada para alguma linguagem de programação, acredito. Vou editar para ficar mais claro.
Jpmc26
3
Pesquisei programadores , estouro de pilha , ciência da computação , matemática e engenharia . A única pergunta que encontrei foi o valor do ponto flutuante zero negativo? . Esta não pode ser apenas a segunda vez que isso acontece!
Estou realmente surpreso que números complexos não tenham surgido nas respostas, especialmente considerando o exemplo da raiz quadrada que apontei.
Jpmc26

Respostas:

69

Você deve ter em mente que, na aritmética da FPU, 0 não necessariamente significa exatamente zero, mas também valor muito pequeno para ser representado usando o tipo de dados fornecido, por exemplo,

a = -1 / 1000000000000000000.0

a é muito pequeno para ser representado corretamente por float (32 bits); portanto, é "arredondado" para -0.

Agora, digamos que nosso cálculo continue:

b = 1 / a

Como a é float, resultará em -infinity, que está bem longe da resposta correta de -1000000000000000000.0.

Agora vamos calcular b se não houver -0 (então a é arredondado para +0):

b = 1 / +0
b = +infinity

O resultado está errado novamente por causa do arredondamento, mas agora está "mais errado" - não apenas numericamente, mas mais importante por causa de sinais diferentes (o resultado da computação é + infinito, o resultado correto é -1000000000000000000.0).

Você ainda pode dizer que isso realmente não importa, pois ambos estão errados. O importante é que existem muitas aplicações numéricas em que o resultado mais importante da computação é o sinal - por exemplo, ao decidir virar à esquerda ou à direita na encruzilhada usando algum algoritmo de aprendizado de máquina, você pode interpretar o valor positivo => girar esquerda, valor negativo => vire à direita, a "magnitude" real do valor é apenas "coeficiente de confiança".

qbd
fonte
Você tem alguma idéia sobre se o sinal de sub-fluxo pode ser especialmente importante em cálculos de números imaginários / complexos?
Jpmc26
@qbd: Você sabe quais são essas aplicações numéricas? Eu diria que os programas que acionam e usam +infe -infem operação normal estão com erros.
Björn Lindqvist
@ BjörnLindqvist Se você quiser aplicativos concretos para download - não conheço nenhum. Eu não acho que seja necessariamente de buggy - em vez de float / double, você pode usar algo como BigDecimal com precisão ilimitada. Mas vale a pena quando o programa fornecerá exatamente os mesmos resultados que aquele com float / double, mas com desempenho muito pior?
QbD
Você escreveu "aplicativos numéricos em que o resultado mais importante da computação é o sinal". Acredito nisso, mas não acredito que haja aplicativos bem escritos que dependam de -0 e de valores +infe -inf. Se o seu programa causa um fluxo insuficiente de ponto flutuante, esse é o erro e o que acontece depois não é tão interessante, imho. Ainda faltam exemplos práticos nos quais -0 é útil.
Björn Lindqvist
11
@ BjörnLindqvist Grande parte do x265 é feita em montagem, contando com detalhes obscuros (que dependem da arquitetura da CPU) que poucas pessoas conhecem em nome do desempenho. Está errado? Confiar no padrão amplamente implementado de 30 anos (que chegou para ficar) para um recurso simples e bem entendido em nome do desempenho de repente não parece tão ruim.
Qd
8

Primeiro, como você cria um -0? Existem duas maneiras: (1) faça uma operação de ponto flutuante em que o resultado matemático é negativo, mas tão próximo de zero que é arredondado para zero e não para um número diferente de zero. Esse cálculo dará um -0. (b) Certas operações que envolvem zeros: multiplique um zero positivo por um número negativo ou divida um zero positivo por um número negativo ou negue um zero positivo.

Ter um zero negativo simplifica um pouco a multiplicação e a divisão, o sinal de x * y ou x / y é sempre o sinal de x, exclusivo ou o sinal de y. Sem zero negativo, haveria uma verificação extra para substituir -0 por +0.

Existem algumas situações muito raras em que é útil. Você pode verificar se o resultado de uma multiplicação ou divisão é matematicamente maior que ou menor que zero, mesmo se houver um fluxo insuficiente (desde que você saiba que o resultado não é um zero matemático). Não me lembro de ter escrito código onde faz diferença.

Os compiladores de otimização odeiam -0. Por exemplo, você não pode substituir x + 0,0 por x, porque o resultado não deve ser x se x for -0,0. Você não pode substituir x * 0,0 por 0,0, porque o resultado deve ser -0,0 se x <0 ou x for -0,0.

gnasher729
fonte
7
Eu gostaria que o IEEE-754 tivesse incluído quatro zeros: "exato", infinitesimal positivo, infinitesimal negativo e sem sinal (este último sendo a diferença entre valores indistinguíveis). Isso faria com que muitos axiomas de ponto flutuante funcionassem - entre eles, x + 0,0 equiv x-0,0 equiv x, xy equiv x + (- 1,0) * y e 1,0 / x equiv -1,0 / (- 1,0 * x) [se x for zero positivo, ambos seriam pós-inf; se neg-zero, ambos neg-inf; se exato ou não assinado, ambos NaN].
Supercat
Consegui obter um zero negativo passando -5e 5entrando fmod(). É muito chato para o meu caso de uso.
Aaron Franke
6

C # Duplo em conformidade com IEEE 754

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

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

impressões:

Infinity
-Infinity

na verdade, para explicar um pouco ...

Double d = -0.0; 

Isso significa algo muito mais próximo de d = The Limit of x as x approaches 0-ou The Limit of x as x approaches 0 from the negatives.


Para abordar o comentário de Philipp ...

Basicamente zero negativo significa subfluxo.

Há muito pouco uso prático para zero negativo, se houver ...

por exemplo, este código (novamente C #):

double a = -0.0;
double b = 0.0;

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

produz este resultado:

True
True
0

Para explicar informalmente, todos os valores especiais que um ponto flutuante IEEE 754 pode ter (infinito positivo, infinito negativo, NAN, -0,0) não têm significado no sentido prático. Eles não podem representar nenhum valor físico ou qualquer valor que faça sentido no cálculo do "mundo real". O que eles querem dizer é basicamente isso:

  • infinito positivo significa um estouro no final positivo que um ponto flutuante pode representar
  • infinito negativo significa um excesso na extremidade positiva que um ponto flutuante pode representar
  • zero negativo significa um underflow e os operandos tinham sinais opostos
  • zero positivo pode significar um fluxo insuficiente e os operandos tinham o mesmo sinal
  • NAN significa que seu cálculo é completamente indefinido, como sqrt(-7)ou não possui um limite como 0/0ou comoPositiveInfinity/PositiveInfinity
AK_
fonte
7
Sim, mas por que isso é importante? Você pode fornecer um exemplo prático do mundo real, onde a diferença importa?
Philipp
5

A questão de como isso se relaciona com os cálculos de números complexos realmente está no cerne do motivo pelo qual ambos +0 e -0 existem no ponto flutuante. Se você estuda Análise Complexa, descobre rapidamente que funções contínuas de Complex para Complex geralmente não podem ser tratadas como 'valor único', a menos que se adote a 'ficção educada' de que as saídas formam o que é conhecido como 'superfície de Riemann'. Por exemplo, o logaritmo complexo atribui cada entrada infinitamente a muitas saídas; quando você os conecta para formar uma saída contínua, você acaba com todas as peças reais formando uma superfície de 'saca-rolhas infinito' ao redor da origem. Uma curva contínua que atravessa o eixo real 'para baixo do lado imaginário positivo' e outra curva que 'envolve o polo' e atravessa o eixo real '

Agora aplique isso a um programa numérico que calcula usando ponto flutuante complexo. A ação executada após um determinado cálculo pode ser muito diferente, dependendo de qual 'planilha' o programa está atualmente 'ativado', e o sinal do último resultado calculado provavelmente indica qual 'planilha'. Agora, suponha que o resultado seja zero? Lembre-se, aqui 'zero' realmente significa 'pequeno demais para representar corretamente'. Porém, se o cálculo puder arranjar para preservar o sinal (ou seja, lembre-se de qual 'planilha') quando o resultado for zero, o código poderá verificar o sinal e executar a ação correta, mesmo nessa situação.

PJM
fonte
1

O motivo é mais simples que o normal

É claro que existem muitos hacks que parecem realmente legais e são úteis (como arredondar para -0.0ou +0.0assumir que temos uma representação de int assinado com um sinal de menos / mais no início (eu sei que isso é resolvido pelo código binário U2 em números inteiros geralmente, mas assumem uma representação menos complexa de double):

0 111 = 7
^ sign

E se houver um número negativo?

1 111 = -7

Ok, simples assim. Então, vamos representar 0:

0 000 = 0

Tudo bem também. Mas que tal 1 000? Tem que ser um número proibido? Melhor não.

Então, vamos assumir que existem dois tipos de zero:

0 000 = +0
1 000 = -0

Bem, isso simplificará nossos cálculos e, com sorte, fornecerá alguns recursos adicionais. Portanto, o +0e -0está vindo apenas de questões de representação binária.

Dawid Pura
fonte
6
Se estou lendo isso corretamente, você está apenas dizendo que as pessoas que definem ou implementam os padrões não querem se dar ao trabalho de proibi-lo. Eu não acho que esse raciocínio sustenta o fato de que o complemento de 2 usa a representação "zero negativo" para um número totalmente diferente e não tem representação de zero negativo. Veja o artigo da Wikipedia que eu vinculei.
Jpmc26
11
@ jpmc26 Eu acho que realmente existe alguma verdade nisso, pois não proibir significa não exigir que as implementações tenham um caso especial. Como é, todo número tem um bit de sinal e pode ser negado alternando o bit de sinal. Até os NaNs são assinados e as implementações podem (mas não precisam) escolher um sinal apropriado ao produzir um NaN. Se não existisse zero, negativa, cada cálculo que resultou em 0 precisaria fazer trabalho extra para arrumar o bit de sinal, etc.
Hobbs
4
@ jpmc26 (ou seja, em qualquer outra multiplicação de dois números, o sinal do resultado é o xor do sinal dos multiplicandos e a magnitude é o produto das duas magnitudes. Na vida real, isso funciona para -1 * 0 = - 0. Mas se zero com o bit de sinal invertido fosse algum valor especial diferente de zero, todo produto que pudesse produzir 0 teria que verificar e garantir que não produz esse valor especial por engano.)
hobbs