¿Flotante tiene un cero negativo? (-0f)

Los números de coma flotante IEEE tienen un bit asignado para indicar el signo, lo que significa que técnicamente puede tener diferentes representaciones binarias de cero (+0 y -0). ¿Hay una operación aritmética que podría hacer, por ejemplo, en C, que da como resultado un valor negativo de coma flotante cero?

Esta pregunta está inspirada en otra que cuestionó si puedes comparar de forma segura 0.0f usando == , y me pregunté si hay otras maneras de representar cero que causaría que float1 == 0.0f rompa para valores aparentemente iguales.

[Editar] ¡Por favor, no comenten sobre la seguridad de comparar flotadores para la igualdad! No estoy tratando de agregar a ese montón desbordante de preguntas duplicadas.

De acuerdo con el estándar, existe cero negativo pero es igual a cero positivo. Para casi todos los propósitos, los dos se comportan de la misma manera y muchos consideran que la existencia de un elemento negativo es un detalle de implementación. Sin embargo, hay algunas funciones que se comportan de manera bastante diferente, a saber, división y atan2 :

 #include  #include  int main() { double x = 0.0; double y = -0.0; printf("%.08f == %.08f: %d\n", x, y, x == y); printf("%.08f == %.08f: %d\n", 1 / x, 1 / y, 1 / x == 1 / y); printf("%.08f == %.08f: %d\n", atan2(x, y), atan2(y, y), atan2(x, y) == atan2(y, y)); } 

El resultado de este código es:

 0.00000000 == -0.00000000: 1 1.#INF0000 == -1.#INF0000: 0 3.14159265 == -3.14159265: 0 

Esto significaría que el código manejaría correctamente ciertos límites sin necesidad de un manejo explícito. No es seguro que confiar en esta característica para valores cercanos a los límites sea una buena idea, ya que un simple error de cálculo puede cambiar el signo y hacer que el valor no sea correcto, pero puede aprovecharlo si evita los cálculos que lo harían cambia el signo

¿Hay una operación aritmética que podría hacer, por ejemplo, en C, que da como resultado un valor negativo de coma flotante cero?

Por supuesto:

 float negativeZero = -10.0e-30f * 10.0e-30f; 

El resultado matemáticamente preciso de la multiplicación no es representable como un valor de punto flotante, por lo que se redondea al valor representable más cercano, que es -0.0f .

La semántica del cero negativo está bien definida por el estándar IEEE-754; la única forma real observable en que su comportamiento difiere del cero en la expresión aritmética es que si se divide por ella, obtendrá un signo diferente de infinito. Por ejemplo:

 1.f / 0.f --> +infinity 1.f / -0.f --> -infinity 

Las comparaciones y las sums y restas con -0.f dan el mismo resultado que con +0.f (en el modo de redondeo predeterminado). La multiplicación puede preservar el signo de cero, pero como se señaló, generalmente no es observable.

Hay algunas funciones de la biblioteca matemática cuyo comportamiento puede variar según el signo de cero. Por ejemplo:

 copysignf(1.0f, 0.0f) --> 1.0f copysignf(1.0f,-0.0f) --> -1.0f 

Esto es más común en las funciones complejas:

 csqrtf(-1.0f + 0.0f*i) --> 0.0f + 1.0f*i csqrtf(-1.0f - 0.0f*i) --> 0.0f - 1.0f*i 

En general, sin embargo, no debería preocuparse por el cero negativo.

Sí, cero puede firmarse, pero el estándar requiere cero positivo y negativo para probarse como igual

Hay un par de operaciones aritméticas simples que dan como resultado una respuesta cero negativa (al menos en los sistemas i386 / x64 / ARMv7 / ARMv8 en los que lo probé):

  • -1 * 0
  • 0 / -1

Estos me tomaron por sorpresa cuando escribía un optimizador para simplificar las expresiones aritméticas. La optimización de ” a = b * 0 ” a ” a = 0 ” dará como resultado la respuesta incorrecta (+0) si b resulta ser negativa (la respuesta correcta es -0).

Sí, el flotante tiene un cero negativo, pero no , no tiene que preocuparse por esto al comparar valores de coma flotante.

La aritmética de punto flotante está definida para funcionar correctamente en casos especiales.

Sí, los float tienen cero negativo al igual que otros tipos de coma flotante IEEE, como el double (en sistemas con coma flotante IEEE). Aquí hay un ejemplo en Octave de cómo crearlos; las mismas operaciones funcionan en C. El operador == trata a +0 y -0 de la misma manera, por lo que los ceros negativos no rompen ese tipo de comparación.

Sí, puede tener un +0 y -0 y esos son patrones de bits diferentes (debería fallar la prueba de igualdad). Nunca debe usar == con float, ciertamente no float IEEE. están bien Hay muchas otras preguntas y discusiones sobre este tema, así que no entraré aquí.

Debe tener precaución al hacer comparaciones de igualdad usando flotadores. Recuerde, está tratando de representar un valor decimal en un sistema binario.

¿Es seguro comprobar los valores de punto flotante para la igualdad en 0?

Si debe comparar valores de punto flotante, le sugiero que use algún tipo de tolerancia que sea aceptable para usted float1 <= toleranceVal && float1 >= toleranceVal2 o multiplique por un factor de diez y moldee como un entero. if (!(int)(float1 * 10000)) { .. some stuff .. }

-lm tiene la función signbit () disponible para indicar si un valor es negativo (incluido -0)

este float1 == 0.0f nunca es realmente una comparación segura.

si tienes algo como

 float x = 0.0f; for (int i = 0; i < 10; i++) x += 0.1f; x -= 1.0f; assert (x == 0.0f); 

fallará aunque se suponga que es 0.