
Miembro orgulloso
lo sé UIKit
usos CGFloat
debido al sistema de coordenadas independiente de la resolución.
Pero cada vez que quiero comprobar si por ejemplo frame.origin.x
es 0
me hace sentir enfermo:
if (theView.frame.origin.x == 0) {
// do important operation
}
no es CGFloat
vulnerable a falsos positivos cuando se compara con ==
, <=
, >=
, <
, >
? Es un punto flotante y tienen problemas de imprecisión: 0.0000000000041
por ejemplo.
Es Objective-C
manejando esto internamente al comparar o puede suceder que un origin.x
que se lee como cero no se compara con 0
como cierto?

Marca de alto rendimiento
Dado que 0 es exactamente representable como un número de punto flotante IEEE754 (o usando cualquier otra implementación de números fp con los que he trabajado), la comparación con 0 probablemente sea segura. Sin embargo, es posible que lo muerdan si su programa calcula un valor (como theView.frame.origin.x
) que tiene razones para creer que debería ser 0 pero que su cálculo no puede garantizar que sea 0.
Para aclarar un poco, un cálculo como:
areal = 0.0
creará (a menos que su idioma o sistema no funcione) un valor tal que (areal==0.0) devuelva verdadero pero otro cálculo como
areal = 1.386 - 2.1*(0.66)
podría no.
Si puede asegurarse de que sus cálculos producen valores que son 0 (y no solo que producen valores que deberían ser 0), entonces puede continuar y comparar los valores de fp con 0. Si no puede asegurarse en el grado requerido , es mejor ceñirse al enfoque habitual de “igualdad tolerada”.
En los peores casos, la comparación descuidada de los valores fp puede ser extremadamente peligrosa: piense en aviónica, guía de armas, operaciones de centrales eléctricas, navegación de vehículos, casi cualquier aplicación en la que la computación se encuentre con el mundo real.
Para Angry Birds, no tan peligroso.
La pregunta correcta: ¿cómo se comparan puntos en Cocoa Touch?
La respuesta correcta: CGPointEqualToPoint().
Una pregunta diferente: ¿Dos valores calculados son iguales?
La respuesta publicada aquí: No lo son.
¿Cómo comprobar si están cerca? Si desea verificar si están cerca, no use CGPointEqualToPoint(). Pero, no verifique si están cerca. Haz algo que tenga sentido en el mundo real, como verificar si un punto está más allá de una línea o si un punto está dentro de una esfera.
Quiero dar una respuesta un poco diferente a las demás. Son excelentes para responder a su pregunta como se indica, pero probablemente no para lo que necesita saber o cuál es su problema real.
¡El punto flotante en gráficos está bien! Pero casi no hay necesidad de comparar flotadores directamente. ¿Por qué necesitarías hacer eso? Los gráficos usan flotadores para definir intervalos. ¡Y comparar si un flotador está dentro de un intervalo también definido por flotadores siempre está bien definido y solo necesita ser consistente, no exacto o preciso! Siempre que se pueda asignar un píxel (¡que también es un intervalo!), eso es todo lo que necesitan los gráficos.
Entonces, si desea probar si su punto está fuera de un [0..width[ range this is just fine. Just make sure you define inclusion consistently. For example always define inside is (x>=0 && x < width). The same goes for intersection or hit tests.
However, if you are abusing a graphics coordinate as some kind of flag, like for example to see if a window is docked or not, you should not do this. Use a boolean flag that is separate from the graphics presentation layer instead.
[The ‘right answer’ glosses over selecting K
. Selecting K
ends up being just as ad-hoc as selecting VISIBLE_SHIFT
but selecting K
is less obvious because unlike VISIBLE_SHIFT
it is not grounded on any display property. Thus pick your poison – select K
or select VISIBLE_SHIFT
. This answer advocates selecting VISIBLE_SHIFT
and then demonstrates the difficulty in selecting K
]
Precisamente debido a los errores de ronda, no debe usar la comparación de valores ‘exactos’ para operaciones lógicas. En su caso específico de una posición en una pantalla visual, no puede importar si la posición es 0.0 o 0.0000000003; la diferencia es invisible para el ojo. Entonces tu lógica debería ser algo como:
#define VISIBLE_SHIFT 0.0001 // for example
if (fabs(theView.frame.origin.x) < VISIBLE_SHIFT) { /* ... */ }
Sin embargo, al final, ‘invisible a la vista’ dependerá de las propiedades de visualización. Si puede establecer un límite superior en la pantalla (debería poder hacerlo); entonces escoge VISIBLE_SHIFT
ser una fracción de ese límite superior.
Ahora, la ‘respuesta correcta’ se basa en K
así que exploremos la elección K
. La ‘respuesta correcta’ anterior dice:
K es una constante que usted elige de tal manera que el error acumulado de sus cálculos está definitivamente limitado por K unidades en el último lugar (y si no está seguro de haber acertado con el cálculo del límite de error, haga que K sea un poco más grande que sus cálculos). decir que debería ser)
Así que necesitamos K
. si conseguir K
es más difícil, menos intuitivo que seleccionar mi VISIBLE_SHIFT
entonces usted decidirá lo que funciona para usted. Encontrar K
vamos a escribir un programa de prueba que analice un montón de K
valores para que podamos ver cómo se comporta. Debería ser obvio cómo elegir K
, si la ‘respuesta correcta’ es utilizable. ¿No?
Vamos a utilizar, como detalles de la ‘respuesta correcta’:
if (fabs(x-y) < K * DBL_EPSILON * fabs(x+y) || fabs(x-y) < DBL_MIN)
Probemos todos los valores de K:
#include <math.h>
#include <float.h>
#include <stdio.h>
void main (void)
{
double x = 1e-13;
double y = 0.0;
double K = 1e22;
int i = 0;
for (; i < 32; i++, K = K/10.0)
{
printf ("K:%40.16lf -> ", K);
if (fabs(x-y) < K * DBL_EPSILON * fabs(x+y) || fabs(x-y) < DBL_MIN)
printf ("YES\n");
else
printf ("NO\n");
}
}
ebg@ebg$ gcc -o test test.c
ebg@ebg$ ./test
K:10000000000000000000000.0000000000000000 -> YES
K: 1000000000000000000000.0000000000000000 -> YES
K: 100000000000000000000.0000000000000000 -> YES
K: 10000000000000000000.0000000000000000 -> YES
K: 1000000000000000000.0000000000000000 -> YES
K: 100000000000000000.0000000000000000 -> YES
K: 10000000000000000.0000000000000000 -> YES
K: 1000000000000000.0000000000000000 -> NO
K: 100000000000000.0000000000000000 -> NO
K: 10000000000000.0000000000000000 -> NO
K: 1000000000000.0000000000000000 -> NO
K: 100000000000.0000000000000000 -> NO
K: 10000000000.0000000000000000 -> NO
K: 1000000000.0000000000000000 -> NO
K: 100000000.0000000000000000 -> NO
K: 10000000.0000000000000000 -> NO
K: 1000000.0000000000000000 -> NO
K: 100000.0000000000000000 -> NO
K: 10000.0000000000000000 -> NO
K: 1000.0000000000000000 -> NO
K: 100.0000000000000000 -> NO
K: 10.0000000000000000 -> NO
K: 1.0000000000000000 -> NO
K: 0.1000000000000000 -> NO
K: 0.0100000000000000 -> NO
K: 0.0010000000000000 -> NO
K: 0.0001000000000000 -> NO
K: 0.0000100000000000 -> NO
K: 0.0000010000000000 -> NO
K: 0.0000001000000000 -> NO
K: 0.0000000100000000 -> NO
K: 0.0000000010000000 -> NO
Ah, entonces K debe ser 1e16 o mayor si quiero que 1e-13 sea ‘cero’.
Entonces, yo diría que tienes dos opciones:
- Haga un cálculo simple de épsilon usando su juicio de ingeniería por el valor de ‘épsilon’, como he sugerido. Si está haciendo gráficos y ‘cero’ está destinado a ser un ‘cambio visible’, entonces examine sus activos visuales (imágenes, etc.) y juzgue qué épsilon puede ser.
- No intente ningún cálculo de punto flotante hasta que haya leído la referencia de la respuesta que no es de culto (y haya obtenido su doctorado en el proceso) y luego use su juicio no intuitivo para seleccionar
K
.
Es principalmente un problema para valores no enteros, donde los errores de redondeo ocurren fácilmente. escribió un entrada en el blog que describe cuándo ocurren los errores de redondeo y cómo estimar el tamaño de los posibles errores.
– Hampus
23 de enero de 2021 a las 7:22