Domanda

In base alla documentazione, decimal.Round utilizza un algoritmo round-to-even che non è comune per la maggior parte delle applicazioni. Quindi finisco sempre per scrivere una funzione personalizzata per eseguire l'algoritmo round-half-up più naturale:

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;
}

Qualcuno conosce il motivo dietro questa decisione di progettazione del framework?

Esiste una implementazione integrata dell'algoritmo round-half-up nel framework? O forse qualche API Windows non gestita?

Potrebbe essere fuorviante per i principianti che scrivono semplicemente decimal.Round (2.5m, 0) aspettandosi 3 come risultato ma ottenendo invece 2.

È stato utile?

Soluzione

Probabilmente perché è un algoritmo migliore. Nel corso di molti arrotondamenti effettuati, calcolerai la media del fatto che tutti gli 0,5 vanno a finire ugualmente su e giù. Ciò fornisce stime migliori dei risultati effettivi, ad esempio, aggiungendo un gruppo di numeri arrotondati. Direi che anche se non è quello che alcuni potrebbero aspettarsi, è probabilmente la cosa più corretta da fare.

Altri suggerimenti

L'altro risponde con i motivi per cui l'algoritmo del banchiere (ovvero arrotondare la metà a pari ) è una buona scelta sono abbastanza corretti. Non soffre di parzialità negativa o positiva tanto quanto il metodo arrotondato a metà dello zero sulle distribuzioni più ragionevoli.

Ma la domanda era perché .NET utilizza l'arrotondamento effettivo di Banker come predefinito - e la risposta è che Microsoft ha seguito la IEEE 754 standard. Questo è anche menzionato in MSDN for Math.Round sotto Note.

Si noti inoltre che .NET supporta il metodo alternativo specificato da IEEE fornendo l'enumerazione MidpointRounding . Naturalmente avrebbero potuto fornire più alternative per risolvere i legami, ma hanno scelto di soddisfare lo standard IEEE.

Anche se non posso rispondere alla domanda di " Perché i designer di Microsoft hanno scelto questo come predefinito? " ;, voglio solo sottolineare che una funzione aggiuntiva non è necessaria.

Math.Round consente di specificare un MidpointRounding :

  • ToEven: quando un numero si trova a metà strada tra altri due, viene arrotondato verso il numero pari più vicino.
  • AwayFromZero - Quando un numero si trova a metà strada tra altri due, viene arrotondato verso il numero più vicino che è lontano da zero.

I decimali vengono utilizzati principalmente per denaro ; L'arrotondamento del banchiere è comune quando si lavora con denaro . O potresti dire.

  

Sono soprattutto i banchieri che hanno bisogno del   tipo decimale; quindi lo fa   & # 8220; arrotondamento del banchiere & # 8221;

L'arrotondamento dei banchieri ha il vantaggio che in media otterrai lo stesso risultato se:

  • arrotondare una serie di & # 8220; linee di fattura & # 8221; prima di aggiungerli,
  • o aggiungili, quindi arrotonda il totale

L'arrotondamento prima di sommare ha risparmiato molto lavoro nei giorni precedenti i computer.

(Nel Regno Unito quando siamo andati le banche decimali non avrebbero gestito la metà di pence, ma per molti anni c'era ancora una moneta di metà pence e il negozio aveva spesso prezzi che terminavano in metà pence & # 8211; così tanti arrotondamenti)

Usa un altro sovraccarico della funzione Round in questo modo:

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

Verrà visualizzato 3 . E se usi

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

otterrai l'arrotondamento del banchiere.

Inoltre, tieni presente che " arrotondamento " tramite una stringa di formato (come " 0 ") produce un risultato diverso da " Math.Round () " ;. Vale a dire, quel 5, .5, ecc. È sempre arrotondato per eccesso:

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top