¿Por qué .NET usa el redondeo bancario como predeterminado?

271

Según la documentación, el decimal.Roundmétodo utiliza un algoritmo de redondeo a par que no es común para la mayoría de las aplicaciones. Así que siempre termino escribiendo una función personalizada para hacer el algoritmo más natural de redondeo a la mitad:

public static decimal RoundHalfUp(this decimal d, int decimals)
{
    if (decimals < 0)
    {
        throw new ArgumentException("The decimals must be non-negative", 
            "decimals");
    }

    decimal multiplier = (decimal)Math.Pow(10, decimals);
    decimal number = d * multiplier;

    if (decimal.Truncate(number) < number)
    {
        number += 0.5m;
    }
    return decimal.Round(number) / multiplier;
}

¿Alguien sabe la razón detrás de esta decisión de diseño del marco?

¿Existe alguna implementación incorporada del algoritmo de redondeo a la mitad en el marco? ¿O tal vez alguna API de Windows no administrada?

Podría ser engañoso para los principiantes que simplemente escriben decimal.Round(2.5m, 0)esperando 3 como resultado, pero obteniendo 2 en su lugar.

Darin Dimitrov
fuente
105
El redondeo no es "más natural". La naturaleza no tiene nada que ver con eso. Es simplemente lo que aprendiste en la escuela primaria cuando aprendiste el concepto de "redondeo". Las lecciones de la escuela primaria no siempre pintan una imagen completa.
Rob Kennedy el
45
@Rob Y es por eso que es más natural , a pesar de que no es correcto
Pacerier
10
No entiendo, @Pacerier. He explicado por qué es no natural, y usted dice que es, de hecho, por lo que es natural. ¿Cómo funciona mi argumento en contra de mi conclusión, que es lo opuesto al suyo? Las cosas a las que te has acostumbrado pueden parecer naturales, y algunas veces decimos en sentido figurado que algo es "segunda naturaleza", pero eso no las hace naturales.
Rob Kennedy el
16
@ Rob, digo que es natural, porque se siente natural. Usted sabe que hay 36 objetos diferentes con el mismo nombre de variable natural, ¿verdad?
Pacerier
9
la naturaleza es definitivamente análoga, por lo que es la palabra incorrecta para usar; Pero esto es ser pedante. Quizás 'usual' sería una mejor palabra para usar ... "¿
Cuál

Respuestas:

196

Probablemente porque es un mejor algoritmo. En el transcurso de muchos redondeos realizados, promediarás que todos los .5 terminan redondeándose por igual hacia arriba y hacia abajo. Esto proporciona mejores estimaciones de los resultados reales si está, por ejemplo, agregando un montón de números redondeados. Diría que, aunque no es lo que algunos pueden esperar, probablemente sea lo más correcto.

Kibbee
fuente
71
suponiendo que tiene una distribución plana de entradas pares e impares, por supuesto
jk.
77
+1 para un mejor algoritmo , aunque Ostemar tiene la respuesta real ( stackoverflow.com/questions/311696/… )
Ian Boyd
2
@ Ian, doy esa respuesta +1 también. De todos modos podemos obtener la "respuesta aceptada movida" Tal vez el OP pueda hacer esto. La respuesta real a "por qué" usa este método está a la mitad de la página. Aunque me gusta bastante el aumento de repeticiones, recibo una vez por semana de esta respuesta.
Kibbee
@ Kibbee: gracias por impulsar mi respuesta. ¿Supongo que depende del OP cambiar la respuesta aceptada como lo considere conveniente?
Ostemar
-1 para afirmar que es un mejor algoritmo. - Dada una muestra aleatoria de números usando el redondeo del banco, terminará teniendo más números en posiciones pares que en posiciones impares. - Es solo después de promediar esos números que obtiene de nuevo una distribución similar a la distribución original. - Sin embargo, si, por ejemplo, traza estos datos en un diagrama de dispersión, uno podría ver la agrupación artificial.
paul23
437

Las otras respuestas con razones por las cuales el algoritmo de Banker (también conocido como la mitad redonda o incluso ) es una buena opción son bastante correctas. No sufre sesgos negativos o positivos tanto como la mitad redonda del método cero respecto de las distribuciones más razonables.

Pero la pregunta era por qué .NET usa el redondeo real de Banker como predeterminado, y la respuesta es que Microsoft ha seguido el estándar IEEE 754 . Esto también se menciona en MSDN para Math.Round en Comentarios.

También tenga en cuenta que .NET admite el método alternativo especificado por IEEE al proporcionar la MidpointRoundingenumeración. Por supuesto, podrían haber proporcionado más alternativas para resolver los lazos, pero eligen cumplir con el estándar IEEE.

Ostemar
fuente
17
Entonces, ¿por qué IEEE 754 sigue el redondeo de los banqueros? Esta respuesta (aún buena) simplemente pasa el balde.
Henk Holterman
44
@HenkHolterman Probablemente debido a lo que se menciona en las otras respuestas (y como resumí); no sufre (demasiado) de sesgo negativo o positivo y, como tal, constituye un valor predeterminado más razonable para la mayoría de las distribuciones y dominios problemáticos.
Ostemar
Creo que es interesante porque IEEE 754 es el estándar para números de coma flotante de los cuales Decimal no lo es. Tal vez siga a IEEE 754 para que se use el mismo algoritmo para redondear Doble como decimal.
Brandon Barkley
1
@BrandonBarkley Un decimal o decimal es un número de coma flotante, y IEEE 754 incluye números de coma flotante decimal .
Pablo H
88

Si bien no puedo responder a la pregunta "¿Por qué los diseñadores de Microsoft eligieron esto como predeterminado?", Solo quiero señalar que no es necesaria una función adicional.

Math.Roundle permite especificar un MidpointRounding:

  • ToEven: cuando un número está a medio camino entre otros dos, se redondea hacia el número par más cercano.
  • AwayFromZero: cuando un número está a medio camino entre otros dos, se redondea hacia el número más cercano que está lejos de cero.
Michael Stum
fuente
8
Y como he mencionado en los hilos relacionados, asegúrese de que sea coherente en su redondeo: si a veces realiza el redondeo en la base de datos y, a veces, en .net, tendrá errores extraños de un centavo que le llevarán semanas hasta descifrar.
Chris
59
Una vez, un cliente me pagó más de $ 40,000 para rastrear un error de redondeo de $ 0.11 entre dos números que estaban a menos de 1 BILLÓN de dólares; los $ 0.11 se debieron a una diferencia en el error de redondeo de 8 dígitos entre un mainframe y SQL Server. ¡Habla de un perfeccionista!
EJ Brennan
12
@EJB - Probablemente sería un perfeccionista si estuviera tratando con mil millones de dólares ;-)
royse41
12
@EJ Brennan: ¿Te tomó $ 40k descubrirlo? Veo problemas como este todo el tiempo, el redondeo es la causa n. ° 1, la normalización doble / flotante es la causa n. ° 2, el error del programador n. ° 3 - el n. ° 3 puede establecerse inmediatamente en el n. ° 1 si no hay casos de prueba predefinidos. Por cierto, ¿pueden ponerme en contacto con su cliente billonario? ¡Creo que también podría encontrar algunos errores más de $ 40k en su sistema! : D
3
@seanxe: O si hubieras visto Office Space. En serio, cada vez que veas inexactitudes misteriosas y pequeñas en el dinero, casi siempre es una buena idea resolver el misterio de cómo están sucediendo exactamente. Es posible que decida no corregir los errores, pero conocer la causa subyacente aún tiene valor. Apuesto a que muchas personas que trabajan con dinero están felices de notar incluso pequeñas inexactitudes.
Brian
22

Los decimales se usan principalmente por dinero ; El redondeo bancario es común cuando se trabaja con dinero . O podrías decirlo.

En su mayoría, los banqueros necesitan el tipo decimal; por lo tanto, hace "redondeo bancario"

El redondeo de los banqueros tiene la ventaja de que, en promedio, obtendrá el mismo resultado si:

  • redondear un conjunto de "líneas de factura" antes de sumarlas,
  • o sumarlos y luego redondear el total

El redondeo antes de sumar ahorró mucho trabajo en los días anteriores a las computadoras.

(En el Reino Unido, cuando fuimos a los bancos decimales, no manejaban medio penique, pero durante muchos años todavía había una moneda de medio penique y la tienda a menudo tenía precios que terminaban en medio penique, así que muchos redondeos)

Ian Ringrose
fuente
8
"Los decimales se usan principalmente para el dinero" ... y todo lo demás que no es un número entero.
John Tyree
3
@JohnTyree, no es cierto la mayoría de las veces se usa un doble / flotante cuando no es un entero ver stackoverflow.com/questions/2545567/…
Ian Ringrose
3
Guau. Error ridículo de mi parte. Decmials, si. Decimales, no. Para la posteridad, estoy de acuerdo con el sentimiento original aquí.
John Tyree
A los banqueros les puede gustar el redondeo de los banqueros, pero los tenedores de libros pueden no ser fanáticos de esto, dicen que la diferencia de 0.005 debería redondear para redondear en 0.01, sin depender de si es un número impar o par.
JustAMartin
0

Use otra sobrecarga de la función Round como esta:

decimal.Round(2.5m, 0,MidpointRounding.AwayFromZero)

Saldrá 3 . Y si usas

decimal.Round(2.5m, 0,MidpointRounding.ToEven)

Obtendrá redondeo de banquero.

Omid Sadeghi
fuente
44
Esto no responde a la pregunta de por qué Banker's Rounding fue elegido como predeterminado.
shortstuffsushi