2012-02-09 27 views
7

Estoy tratando de hacer algunas comparaciones de números y obtengo algunos resultados raros.NSNumber comparar: devolver resultados diferentes

NSNumber* number1 = [NSNumber numberWithFloat:1.004]; 
NSNumber* number2 = [NSNumber numberWithDouble:1.004]; 

([number1 compare:number2] == NSOrderedSame) ? NSLog(@"YES") : NSLog(@"NO"); 
([number1 compare:number2] == NSOrderedAscending) ? NSLog(@"YES") : NSLog(@"NO"); 
([number1 doubleValue] == [number2 doubleValue]) ? NSLog(@"YES") : NSLog(@"NO"); 
([number1 floatValue] == [number2 floatValue]) ? NSLog(@"YES") : NSLog(@"NO"); 

salida del registro:

NO
SÍ NO

Esto es muy frustrante para mí. Sé que esto es probablemente porque la diferencia entre el número de bits en un flotante en comparación con un doble. Me parece que está truncando el doble hacia abajo para hacer una comparación. Pero si no sé cómo se crea el número, ¿cómo obtengo los resultados correctos? ¿Hay alguna manera diferente de comparar NSNumber?

+3

¿Qué pasa si se utiliza '[número1 isEqualToNumber: number2] ', esa es la forma preferida, al menos si solo estás tratando de determinar la igualdad de valores? – twilson

+1

Lo siento Twilson, que todavía devuelve NO. Los números SON diferentes. – picciano

Respuesta

7

He intentado utilizar isEqualToNumber: y que no hay ningún. La razón por la que no son lo mismo es porque 1.004 no se puede representar exactamente en binario. La aproximación double tiene más dígitos después del punto decimal que la aproximación float, por lo que los dos números son diferentes. Normalmente, cuando se comparan los números de punto flotante, se prueba para ver si son iguales dentro de un valor de tolerancia fabs(a - b) < tolerance:

NSNumber* number1 = [NSNumber numberWithFloat:1.004]; 
NSNumber* number2 = [NSNumber numberWithDouble:1.004]; 

NSLog(@"number 1: %.12f", [number1 doubleValue]); 
NSLog(@"number 2: %.12f", [number2 doubleValue]); 

([number1 isEqualToNumber:number2]) ? NSLog(@"YES") : NSLog(@"NO"); 
fabs([number1 floatValue] - [number2 doubleValue]) < 1.0e-7 ? NSLog(@"YES") : NSLog(@"NO"); 

resultados:

2012-02-09 15:08:34.272 so9219935[3313:903] number 1: 1.003999948502 
2012-02-09 15:08:34.274 so9219935[3313:903] number 2: 1.004000000000 
2012-02-09 15:08:34.275 so9219935[3313:903] NO 
2012-02-09 15:08:34.275 so9219935[3313:903] YES 
+0

¡Muy buena solución! Me parece una locura que Apple no maneje esto con el método compare: –

+5

Apple (o, más exactamente, los escritores del compilador) no pueden hacer suposiciones sobre la tolerancia del programador a la igualdad. Todo lo que pueden hacer es exactamente lo que el programador les dijo que hicieran. En este caso, estabas comparando dos valores desiguales.Los números de coma flotantes no son un tema trivial. Si tiene algo de tiempo, eche un vistazo a [Lo que todo científico informático debe saber sobre la aritmética de coma flotante] (http://www-users.math.umd.edu/~jkolesar/mait613/floating_point_math.pdf). – SSteve

+0

¡Gracias por la referencia! He estado buscando una lectura recomendada sobre esto. He sabido sobre este tema por un tiempo, simplemente no he hecho nada al respecto. :) –

2

El método compare: sigue las reglas C estándar para la conversión de tipos. Por ejemplo, si compara un objeto NSNumber que tiene un valor entero con un objeto NSNumber que tiene un valor de coma flotante, el valor entero se convierte en un valor de punto flotante para la comparación.

Además, al inicializar un NSNumber con un flotador, la conversión a un doble pierde cierta precisión.

NSNumber* number1 = [NSNumber numberWithFloat:1.004]; 
NSNumber* number2 = [NSNumber numberWithDouble:1.004]; 
NSLog(@"number1 double: %1.16f", [number1 doubleValue]); 
NSLog(@"number2 double: %1.16f", [number2 doubleValue]); 
NSLog(@"number1 objCType %s", [number1 objCType]); 
NSLog(@"number2 objCType %s", [number2 objCType]); 

2012-02-09 15:59:49.487 testNSNumber[89283:f803] number1 double: 1.0039999485015869 
2012-02-09 15:59:49.488 testNSNumber[89283:f803] number2 double: 1.0040000000000000 
2012-02-09 16:21:01.655 testNSNumber[4351:f803] number1 objCType f 
2012-02-09 16:21:01.656 testNSNumber[4351:f803] number2 objCType d 

Si usted sabe que puede tener una mezcla de flotador y dobles, una solución es comparar los floatValues ​​NSNumber como lo hizo en la última línea de su fragmento de código en su pregunta.

Además, puede obtener el tipo de datos en NSNumber con el método objCType.

+0

Me gusta mucho objType! ¡Gracias! –

2

intenta lo isEqualToNumber: método para verificar su condición

([number1 isEqualToNumber:number2]) ? NSLog(@"YES") : NSLog(@"NO"); 

this stackoverflow question también da una respuesta detallada

+0

Esto no funcionará como se espera y registrará "NO". NSNumbers inicializados con flotante y dobles serán valores diferentes. Ver mi respuesta arriba. La pregunta SO a la que hace referencia está usando todas las carrozas. – picciano

Cuestiones relacionadas