2011-02-14 17 views
9

He aquí una pequeña joya directamente de mi VBE (MS Excel 2007 VBA):¿Por qué CLng produce resultados diferentes?

?clng(150*0.85) 
127 
x = 150*0.85 
?clng(x) 
128 

Puede alguien explicar este comportamiento? En mi humilde opinión, la primera expresión debería arrojar 128 (.5 redondeado a la par más próxima) o, al menos, ambos resultados deberían ser iguales.

+1

Weird. 'CLng (CDbl (150 * 0.85))' lo arregla. 'Round (150 * 0.85)' también. – wqw

+0

También lo hace CSng. Según mi teoría, fue un error de coma flotante. –

Respuesta

11

Creo wqw es correcto, pero yo voy a dar los detalles.

En la declaración clng(150 * 0.85), 150 * 0.85 se calcula en ampliada precisión:

150 = 1.001011 x 2^7 

0.85 en doble precisión =

1.1011001100110011001100110011001100110011001100110011 x 2^-1 

multiplicar estos con la mano y se obtiene

1.1111110111111111111111111111111111111111111111111111110001 x 2^6 = 
127.4999999999999966693309261245303787291049957275390625 

Eso es 59 bits, que se adapta cómodamente a ext fin de precisión. Es menos que 127.5 así que redondea hacia abajo.

En la declaración x = 150 * 0.85, ese valor 59 bit se redondea a 53 bits, dando

1.1111111 x 2^6 = 1111111.1 = 127.5 

Así que repasa según redonda medio-a-par.

(Vea mi artículo http://www.exploringbinary.com/when-doubles-dont-behave-like-doubles/ para más información.)

2

Ahh una de las cosas "divertidas" sobre VBA es el redondeo en CInt(), etc. Es lo que se denomina redondeo bancario. El redondeo bancario es donde los valores de 0,5 se redondean hacia arriba o hacia abajo dependiendo de si el número es un número par, por lo que 2.5 redondea a 2, 3.5 a 4, y así sucesivamente.

Se puede encontrar más aquí sobre el redondeo

http://www.consultdmw.com/rounding-numbers.htm

+0

Él menciona que debe redondearse al más cercano incluso en la pregunta. –

+0

En este caso, 127 no es el "par más cercano". Esto no explica nada. – wqw

1

Esto es un poco de una suposición, pero no son necesariamente 0.85 representable como un número de coma flotante. Si está desactivado por 0.0000000000001, puede afectar el redondeo de maneras extrañas.

Si usa CDec (.85) para forzarlo a modo decimal, no obtiene esa rareza. Esta es una de las muchas razones por las que no uso single/double donde la precisión es importante.

+0

El primer resultado podría provenir del error de redondeo (.4999 ... en lugar de .5), pero en este caso el segundo resultado debería ser 127 también. En mi comprensión, la asignación intermedia a x no debería afectar el resultado del lanzamiento. –

0

Ambos lo que Kevin y Jonathan han dicho son ciertos, pero la respuesta de Jonathan es más aplicable aquí. Si estuviera tratando con números de moneda en lugar de coma flotante, entonces se aplicaría la regla de redondeo del banquero.

+0

CLng SE APLICA Bankers redondeando –

+1

Sí, pero eso no importa si lo que está aplicando el redondeo ya es un número de punto flotante binario que incluye un error de redondeo con respecto a la representación decimal. El número ya estará ligeramente por encima o por debajo del punto de 1/2 sentido, por lo que nunca se aplicará la regla que el algoritmo de redondeo utilice para resolver los valores de 1/2. –

1

Mi teoría es que VBA/VB6 está utilizando x87 para los cálculos de punto flotante y esto convierte implícitamente los dobles a una mayor precisión si se obtienen 80 bits para los resultados intermedios. Por lo tanto, una asignación a v o conversión explícita con CDbl convierte el valor intermedio de 80 bits en 64 bits redondeándolo (o truncándolo).

Aquí es un poco de discusión:

Extended (80-bit) double floating point in x87, not SSE2 - we don't miss it?

Cuestiones relacionadas