Pregunta

Según la documentación, el decimal.Round 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 integrada 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.

¿Fue útil?

Solución

Probablemente porque es un algoritmo mejor. 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.

Otros consejos

El otro responde con razones por las cuales el algoritmo de Banker (también conocido como media vuelta para igualar ) Es una buena elección son bastante correctas. No sufre sesgos negativos o positivos tanto como el medio redondeo lejos de cero sobre 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 for Math.Round en Observaciones.

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

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

Math.Round le 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.

Los decimales se utilizan principalmente para dinero ; El redondeo del banco es común cuando se trabaja con dinero . O podrías decir.

  

Son principalmente los banqueros los que necesitan   tipo decimal; por lo tanto lo hace   & # 8220; banquero & # 8217; s redondeo & # 8221;

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

  • redondear un conjunto de & # 8220; líneas de factura & # 8221; antes de sumarlos,
  • 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 se ocupaban de 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 & # 8211; así que muchos redondeos)

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

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

Producirá 3 . Y si usas

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

obtendrá redondeo bancario.

Además, tenga en cuenta que " redondeo " a través de una cadena de formato (como " 0 ") produce un resultado diferente que " Math.Round () " ;. Es decir, que 5, .5, etc. siempre se redondea:

let d, d' = 2.5, 3.5

Debug.WriteLine(Math.Round(d))      // 2.5 -> 2
Debug.WriteLine(d.ToString("0"))    // 2.5 -> 3

Debug.WriteLine(Math.Round(d'))     // 3.5 -> 4
Debug.WriteLine(d'.ToString("0"))   // 3.5 -> 4


let dd, dd' = 2.25, 2.35

Debug.WriteLine(Math.Round(dd, 1))     // 2.25 -> 2.2
Debug.WriteLine(dd.ToString("0.0"))    // 2.25 -> 2.3

Debug.WriteLine(Math.Round(dd', 1))    // 2.35 -> 2.4
Debug.WriteLine(dd'.ToString("0.0"))   // 2.35 -> 2.4
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top