¿Por qué es importante el cero negativo?

64

Estoy confundido acerca de por qué nos importan las diferentes representaciones de cero positivo y negativo.

Recuerdo vagamente las afirmaciones de lectura de que tener una representación negativa de cero es extremadamente importante en la programación que involucra números complejos. Nunca he tenido la oportunidad de escribir código que involucre números complejos, así que estoy un poco desconcertado sobre por qué este sería el caso.

El artículo de Wikipedia sobre el concepto no es especialmente útil; solo hace vagas afirmaciones sobre el cero con signo, lo que simplifica ciertas operaciones matemáticas en coma flotante, si lo entiendo correctamente. Esta respuesta enumera un par de funciones que se comportan de manera diferente, y tal vez se podría inferir algo de los ejemplos si está familiarizado con cómo podrían usarse. (Aunque, el ejemplo particular de las raíces cuadradas complejas parece completamente incorrecto, ya que los dos números son matemáticamente equivalentes, a menos que tenga un malentendido.) Pero no he podido encontrar una declaración clara del tipo de problema en el que te meterías si no estuviera allí. Los recursos más matemáticos que he podido encontrar indican que no hay distinción entre los dos desde una perspectiva matemática, y el artículo de Wikipedia parece sugerir que esto rara vez se ve fuera de la computación, aparte de describir los límites.

Entonces, ¿por qué un cero negativo es valioso en informática? Estoy seguro de que solo me falta algo.

jpmc26
fuente
66
El cero negativo puede indicar un flujo inferior en un número de coma flotante IEEE, pero más allá de eso, su uso parece ser controvertido y oscuro. Si tuviera que adivinar, diría que el cero negativo está representado en coma flotante IEEE porque ... bueno, puedes hacerlo. Para un viaje aún más interesante, busque la información sobre la señal de coma flotante NaN.
Robert Harvey
1
Si el ejemplo particular es "1 / 0.0" / "1 / -0.0", 0 es un corte de rama para 1 / x y el límite depende de si se acerca desde abajo o desde arriba.
Vatine
@Vatine No, el ejemplo particular es sqrt(-1+0i) = iy sqrt(-1-0i) = -i, aunque está vestido con la sintaxis adecuada para algún lenguaje de programación, creo. Lo editaré para ser más claro.
jpmc26
3
Busqué Programadores , Stack Overflow , Informática , Matemáticas e Ingeniería . La única pregunta que pude encontrar fue ¿ Usos para el valor negativo de coma flotante cero? . ¡Esta no puede ser solo la segunda vez que esto ocurre!
Estoy realmente sorprendido de que los números complejos no hayan aparecido en absoluto en las respuestas, especialmente dado el ejemplo de raíz cuadrada que señalé.
jpmc26

Respuestas:

69

Debe tener en cuenta que en la aritmética de FPU, 0 no necesariamente tiene que significar exactamente cero, sino que también tiene un valor demasiado pequeño para ser representado usando un tipo de datos dado, por ejemplo

a = -1 / 1000000000000000000.0

a es demasiado pequeño para ser representado correctamente por flotante (32 bits), por lo que está "redondeado" a -0.

Ahora, digamos que nuestro cálculo continúa:

b = 1 / a

Debido a que a es flotante, dará como resultado un infinito que está bastante lejos de la respuesta correcta de -1000000000000000000.0

Ahora calculemos b si no hay -0 (entonces a se redondea a +0):

b = 1 / +0
b = +infinity

El resultado es incorrecto nuevamente debido al redondeo, pero ahora está "más equivocado", no solo numéricamente, sino más importante debido a un signo diferente (el resultado del cálculo es + infinito, el resultado correcto es -1000000000000000000.0).

Aún se podría decir que realmente no importa, ya que ambos están equivocados. Lo importante es que hay muchas aplicaciones numéricas donde el resultado más importante del cálculo es el signo, por ejemplo, al decidir si girar a la izquierda o la derecha en la encrucijada utilizando algún algoritmo de aprendizaje automático, puede interpretar un valor positivo => girar izquierda, valor negativo => girar a la derecha, la "magnitud" real del valor es solo "coeficiente de confianza".

qbd
fuente
¿Tiene alguna idea sobre si el signo de flujo inferior puede ser especialmente importante en los cálculos de números imaginarios / complejos?
jpmc26
@qbd: ¿Sabes cuáles son esas aplicaciones numéricas? Diría que los programas que desencadenan y usan +infy -infen funcionamiento normal tienen errores.
Björn Lindqvist
@ BjörnLindqvist Si desea aplicaciones concretas y descargables, entonces no conozco ninguna. No creo que sea necesariamente defectuoso: en lugar de flotante / doble, podría usar algo como BigDecimal con precisión ilimitada. ¿Pero vale la pena cuando el programa dará exactamente los mismos resultados que el que tiene flotante / doble, pero con un rendimiento mucho peor?
qbd
Usted escribió "aplicaciones numéricas donde el resultado más importante de la computación es el signo". Puedo creerlo, pero no puedo creer que haya aplicaciones bien escritas que se basen en -0 y en los valores +infy -inf. Si su programa causa un flujo inferior de coma flotante, ese es el error y lo que sucede después no es tan interesante, en mi opinión. Todavía nos faltan ejemplos prácticos en los que -0 es útil.
Björn Lindqvist
1
@ BjörnLindqvist Gran parte de x265 se realiza en ensamblado, confiando en sus detalles oscuros (que dependen de la arquitectura de la CPU) que pocas personas conocen en nombre del rendimiento. ¿Esta mal? Confiar en el estándar de 30 años ampliamente implementado (que llegó para quedarse) para una característica simple y bien entendida en nombre del rendimiento de repente no parece tan malo.
qbd
8

Primero, ¿cómo creas un -0? Hay dos formas: (1) hacer una operación de punto flotante donde el resultado matemático es negativo, pero tan cercano a cero que se redondea a cero y no a un número distinto de cero. Ese cálculo dará un -0. (b) Ciertas operaciones que involucran ceros: multiplique un cero positivo por un número negativo, o divida un cero positivo por un número negativo, o niegue un cero positivo.

Tener un cero negativo simplifica un poco la multiplicación y la división, el signo de x * y o x / y es siempre el signo de x, exclusivo o el signo de y. Sin cero negativo, tendría que haber alguna verificación adicional para reemplazar -0 con +0.

Hay algunas situaciones muy raras en las que es útil. Puede verificar si el resultado de una multiplicación o división es matemáticamente mayor o menor que cero, incluso si hay un flujo inferior (siempre y cuando sepa que el resultado no es un cero matemático). No recuerdo haber escrito un código que marque la diferencia.

Los compiladores optimizadores odian -0. Por ejemplo, no puede reemplazar x + 0.0 con x, porque el resultado no debería ser x si x es -0.0. No puede reemplazar x * 0.0 con 0.0, porque el resultado debería ser -0.0 si x <0 o x es -0.0.

gnasher729
fuente
77
Desearía que IEEE-754 hubiera incluido cuatro ceros: "exacto", infinitesimal positivo, infinitesimal negativo y sin signo (siendo este último la diferencia entre valores indistinguibles). Hacer eso habría hecho que muchos axiomas de coma flotante funcionen, entre ellos, x + 0.0 equiv x-0.0 equiv x, xy equiv x + (- 1.0) * y, y 1.0 / x equiv -1.0 / (- 1.0 * x) [si x es cero positivo, ambos serían pos-inf; si neg-zero, ambos neg-inf; si es exacto o sin firmar, ambos NaN].
supercat
Pude obtener un cero negativo al pasar -5y 5entrar fmod(). Es bastante molesto para mi caso de uso.
Aaron Franke
6

C # Double que cumple con IEEE 754

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

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

huellas dactilares:

Infinity
-Infinity

en realidad para explicar un poco ...

Double d = -0.0; 

Esto significa algo mucho más cercano a d = The Limit of x as x approaches 0-o The Limit of x as x approaches 0 from the negatives.


Para abordar el comentario de Philipp ...

Básicamente cero negativo significa flujo inferior.

Hay muy poco uso práctico para el cero negativo, si lo hay ...

por ejemplo, este código (nuevamente C #):

double a = -0.0;
double b = 0.0;

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

produce este resultado:

True
True
0

Para explicar de manera informal, todos los valores especiales que puede tener un punto flotante IEEE 754 (infinito positivo, infinito negativo, NAN, -0.0) no tienen significado en el sentido práctico. No pueden representar ningún valor físico, ni ningún valor que tenga sentido en el cálculo del "mundo real". Lo que quieren decir es básicamente esto:

  • infinito positivo significa un desbordamiento en el extremo positivo que un punto flotante puede representar
  • infinito negativo significa un desbordamiento en el extremo positivo que un punto flotante puede representar
  • cero negativo significa un flujo inferior y los operandos tenían signos opuestos
  • el cero positivo puede significar un flujo inferior y los operandos tenían el mismo signo
  • NAN significa que su cálculo está indefinidamente complaciente, como sqrt(-7), o no tiene un límite como 0/0o comoPositiveInfinity/PositiveInfinity
ALASKA_
fuente
77
Sí, pero ¿por qué es esto importante? ¿Puede proporcionar un ejemplo práctico del mundo real donde la diferencia es importante?
Philipp
5

La pregunta acerca de cómo se relaciona esto con los cálculos de números complejos realmente llega al corazón de por qué tanto +0 como -0 existen en coma flotante. Si estudia el Análisis complejo, descubrirá rápidamente que las funciones continuas de Complejo a Complejo generalmente no pueden tratarse como 'de un solo valor' a menos que uno adopte la 'ficción cortés' de que las salidas forman lo que se conoce como 'superficie de Riemann'. Por ejemplo, el logaritmo complejo asigna a cada entrada infinitas salidas; cuando los 'conecta' para formar una salida continua, termina con todas las partes reales formando una superficie de 'sacacorchos infinito' alrededor del origen. Una curva continua que cruza el eje real 'hacia abajo desde el lado positivo-imaginario' y otra curva que 'envuelve el polo' y cruza el eje real '

Ahora aplique eso a un programa numérico que calcule usando punto flotante complejo. La acción tomada después de un cálculo dado puede ser muy diferente dependiendo de la "hoja" en la que el programa se encuentra actualmente "encendido", y el signo del último resultado calculado probablemente le indica qué "hoja". Ahora supongamos que el resultado fue cero? Recuerde, aquí 'cero' realmente significa 'demasiado pequeño para representarlo correctamente'. Pero si el cálculo podría hacer arreglos para -conservar el signo- (es decir, recordar qué 'hoja') cuando el resultado es cero, entonces el código puede verificar el signo y realizar la acción correcta incluso en esta situación.

PJM
fuente
1

El motivo es más simple de lo habitual.

Por supuesto, hay muchos hacks que se ven realmente bien y son útiles (como redondear -0.0o +0.0asumir que tenemos una representación de int firmado con un signo menos / más al principio (sé que se resuelve con el código binario U2) en enteros por lo general, pero suponga una representación menos compleja de doble):

0 111 = 7
^ sign

¿Qué pasa si hay un número negativo?

1 111 = -7

Bien, así de simple. Entonces representemos 0:

0 000 = 0

Eso también está bien. ¿Pero que pasa 1 000? ¿Tiene que ser un número prohibido? Mejor no.

Así que supongamos que hay dos tipos de cero:

0 000 = +0
1 000 = -0

Bueno, eso simplificará nuestros cálculos y, de forma fortuita, le dará algunas características adicionales de redondeo. Entonces, +0y -0provienen solo de problemas de representación binaria.

Dawid Pura
fuente
66
Si estoy leyendo esto correctamente, básicamente estás diciendo que las personas que definen o implementan los estándares no querían tomarse la molestia de prohibirlo. No creo que este razonamiento sea válido para el hecho de que el complemento de 2 usa la representación de "cero negativo" para un número completamente diferente y no tiene representación de cero negativo. Vea el artículo de Wikipedia que he vinculado.
jpmc26
1
@ jpmc26 Creo que en realidad hay algo de verdad en eso, ya que no prohibirlo significa que no se requiere que las implementaciones tengan un caso especial. Tal como están las cosas, cada número tiene un bit de signo y se puede negar al alternar el bit de signo. Incluso los NaN están firmados, y las implementaciones pueden (pero no están obligadas a) elegir un signo apropiado al producir un NaN. Si no existiera el cero negativo, todos los cálculos que resultó en 0 tendría que realizar un trabajo extra para arreglar el bit de signo, etc
Hobbs
44
@ jpmc26 (es decir, en cualquier otra multiplicación de dos números, el signo del resultado es el xor del signo de los multiplicandos, y la magnitud es el producto de las dos magnitudes. En la vida real esto funciona para -1 * 0 = - 0. Pero si el cero con el bit de signo invertido era un valor especial distinto de cero, cada producto que podría producir 0 tendría que verificar y asegurarse de que no produce ese valor especial por error.)
hobbs