Perché .NET utilizza l'arrotondamento del banco come predefinito?


271

Secondo la documentazione, il decimal.Roundmetodo 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 sa 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 di conseguenza 3 ma ottenendo invece 2.


105
L'arrotondamento non è "più naturale". La natura non ha nulla a che fare con esso. È semplicemente quello che hai imparato a scuola quando hai imparato il concetto di "arrotondamento". Le lezioni della scuola elementare non sempre dipingono un quadro completo.
Rob Kennedy,

45
@Rob Ed è per questo che è più naturale , anche se non è corretto
Pacerier,

10
Non capisco, @Pacerier. Ho spiegato perché non è naturale e tu dici che in effetti è naturale. In che modo la mia tesi funziona contro la mia conclusione, che è l'opposto della tua? Le cose a cui ti sei abituato potrebbero sembrare naturali, e talvolta diciamo figurativamente che qualcosa è "seconda natura", ma ciò non le rende naturali.
Rob Kennedy,

16
@Rob sto dicendo che è naturale, perché sembra naturale. Sai che ci sono 36 oggetti diversi con lo stesso nome di variabile naturale giusto?
Pacerier,

9
la natura è decisamente analoga, quindi è la parola sbagliata da usare; ma questo è pedante. Forse 'solito' sarebbe una parola migliore da usare .. "qual è il solito arrotondamento che la gente fa"> 0,5 va a 1,0
whytheq

Risposte:


196

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.


71
supponendo che tu abbia una distribuzione piatta di input pari e dispari ovviamente
jk.

7
+1 per un algoritmo migliore , sebbene Ostemar abbia la risposta effettiva ( stackoverflow.com/questions/311696/… )
Ian Boyd,

2
@Ian, do anche quella risposta +1. Ad ogni modo possiamo far spostare la "risposta accettata". Forse l'OP può farlo. La risposta effettiva al "perché" utilizza questo metodo è a metà pagina. Anche se mi piace molto il potenziamento del rep, ricevo circa una volta alla settimana da questa risposta.
Kibbee,

@Kibbee - grazie per aver spinto la mia risposta. Immagino che spetti all'OP cambiare la risposta accettata come ritiene opportuno?
Ostemar,

-1 per affermare che è un algoritmo migliore. - Dato un campione casuale di numeri usando l'arrotondamento del banco, finirai per avere più numeri in posizioni pari che in posizioni dispari. - È solo dopo aver calcolato la media di quei numeri che si ottiene di nuovo uno spread simile alla distribuzione originale. - Tuttavia, se ad esempio si tracciano questi dati in un diagramma a dispersione, si potrebbe vedere un raggruppamento artificiale.
paul23

437

Le altre risposte con i motivi per cui l'algoritmo del banchiere (noto anche come metà round a pari ) è una buona scelta sono del tutto corretti. Non soffre di parzialità negativa o positiva tanto quanto la metà rotonda dal metodo zero rispetto alla maggior parte delle distribuzioni ragionevoli.

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

Si noti inoltre che .NET supporta il metodo alternativo specificato da IEEE fornendo l' MidpointRoundingenumerazione. Naturalmente avrebbero potuto fornire più alternative alla risoluzione dei legami, ma hanno scelto di soddisfare solo lo standard IEEE.


17
Quindi, perché IEEE 754 segue l'arrotondamento dei banchieri? Questa (ancora buona) risposta non fa che sfuggire.
Henk Holterman,

4
@HenkHolterman Probabilmente a causa di ciò che è menzionato nelle altre risposte (e come ho riassunto); non soffre (troppo) di distorsioni negative o positive e come tale rende un default più risonabile per la maggior parte delle distribuzioni e dei domini problematici.
Ostemar,


Penso che sia interessante perché IEEE 754 è lo standard per i numeri in virgola mobile di cui Decimale non lo è. Forse segue IEEE 754 in modo che lo stesso algoritmo venga utilizzato per arrotondare Double come Decimale.
Brandon Barkley,

1
@BrandonBarkley Un decimale o decimale è un numero in virgola mobile e IEEE 754 include numeri in virgola mobile decimali .
Pablo H,

88

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

Math.Roundti permette di specificare un MidpointRounding:

  • ToEven - Quando un numero è 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.

8
E come ho già detto nei thread correlati, assicurati di essere coerente nel tuo arrotondamento - se a volte esegui arrotondamenti nel database e talvolta in .net, avrai strani errori di un centesimo che ti porteranno settimane a risolvere.
chris,

59
Una volta un cliente mi ha pagato oltre $ 40.000 per rintracciare un errore di arrotondamento di $ 0,11 tra due numeri che erano entrambi a meno di 1 MILIONE di dollari; $ 0,11 era dovuto a una differenza nell'errore di arrotondamento dell'ottava cifra tra un mainframe e SQL Server. Parla di un perfezionista!
EJ Brennan,

12
@EJB - probabilmente sarei un perfezionista se avessi a che fare con un miliardo di dollari ;-)
royse41

12
@EJ Brennan: ti ci sono voluti $ 40k per capirlo? Vedo sempre problemi come questo, l'arrotondamento è la causa # 1, la normalizzazione doppio / float è la causa # 2, l'errore del programmatore # 3 - # 3 può essere immediatamente impostato su # 1 se non ci sono casi di test predefiniti. a proposito, puoi per favore mettermi in contatto con il tuo client miliardare, penso che potrei trovare anche qualche altro bug da $ 40k nel suo sistema! : D

3
@seanxe: O se avessi visto Office Space. Scherzi a parte, ogni volta che vedi piccole e misteriose inesattezze nel denaro, risolvere il mistero di come stanno esattamente accadendo è quasi sempre una buona idea. È possibile che tu decida di non correggere i bug, ma conoscere la causa sottostante ha ancora valore. Scommetto che molte persone che lavorano con i soldi sono felici di notare anche piccole imprecisioni.
Brian,

22

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

Sono soprattutto i banchieri che hanno bisogno del tipo decimale; quindi fa "arrotondamento del banchiere"

L'arrotondamento dei banchieri ha il vantaggio di ottenere in media lo stesso risultato se:

  • arrotondare una serie di "righe di fattura" prima di aggiungerle,
  • o aggiungili, quindi arrotonda il totale

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

(Nel Regno Unito quando siamo andati le banche decimali non avrebbero avuto a che fare con mezzo penny, ma per molti anni c'era ancora una moneta da mezzo penny e il negozio aveva spesso prezzi che terminavano a mezzo penny - quindi molti arrotondamenti)


8
"I decimali sono usati principalmente per soldi" ... e tutto il resto che non è un numero intero.
John Tyree,

3
@JohnTyree, Non vero il più delle volte viene usato un double / float quando non è un numero intero. vedi stackoverflow.com/questions/2545567/…
Ian Ringrose il

3
Wow. Errore ridicolo da parte mia. Decmials si. Decimali, no. Per i posteri, sono d'accordo con il sentimento originale qui.
John Tyree,

Ai banchieri potrebbe piacere l'arrotondamento dei banchieri, ma i contabili potrebbero non esserne fan, affermano che la differenza di 0,005 dovrebbe arrotondare per arrotondare per eccesso a 0,01, indipendentemente dal fatto che sia un numero pari o dispari.
JustAMartin,

0

Utilizzare un altro sovraccarico della funzione Round in questo modo:

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

Verrà emesso 3 . E se usi

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

otterrai l'arrotondamento del banchiere.


4
Questo non risponde alla domanda sul perché l'arrotondamento del banco è stato scelto come predefinito.
shortstuffsushi,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.