2010-06-24 6 views
41

J'ai utilisé Math.Round(myNumber, MidpointRounding.ToEven) en C# pour faire mon arrondissement côté serveur, cependant, l'utilisateur doit savoir 'en direct' ce que sera le résultat de l'opération côté serveur (éviter un Ajax request) en créant une méthode JavaScript pour répliquer la méthode MidpointRounding.ToEven utilisée par C#.Arrondi gaussien/banquier en JavaScript

MidpointRounding.ToEven est Gaussian/banker's rounding, une méthode d'arrondissement très courante pour les systèmes comptables décrite here.

Quelqu'un a-t-il une expérience avec ceci? J'ai trouvé des exemples en ligne, mais ils ne le font pas rond à un nombre donnéde décimales ...

+0

Bonne question. Est-ce que [ce script] (http://dansnetwork.com/2009/11/30/round-half-to-even-with-javascript/) est l'un des exemples que vous avez trouvés? On dirait que ça pourrait convenir mais je ne suis pas un expert sur le sujet :-) –

+0

Sa fin! Mais malheureusement, ne fonctionne pas avec des nombres négatifs - je vais faire quelques modifications et poster ici ... Merci :) – Jimbo

Répondre

62
function evenRound(num, decimalPlaces) { 
    var d = decimalPlaces || 0; 
    var m = Math.pow(10, d); 
    var n = +(d ? num * m : num).toFixed(8); // Avoid rounding errors 
    var i = Math.floor(n), f = n - i; 
    var e = 1e-8; // Allow for rounding errors in f 
    var r = (f > 0.5 - e && f < 0.5 + e) ? 
       ((i % 2 == 0) ? i : i + 1) : Math.round(n); 
    return d ? r/m : r; 
} 

console.log(evenRound(1.5)); // 2 
console.log(evenRound(2.5)); // 2 
console.log(evenRound(1.535, 2)); // 1.54 
console.log(evenRound(1.525, 2)); // 1.52 

démonstration en direct: http://jsfiddle.net/NbvBp/

Pour ce qui ressemble à un traitement plus rigoureux de ce (I Je ne l'ai jamais utilisé), vous pouvez essayer cette implémentation BigNumber.

+1

Doucement fait! Fonctionne pour les négatifs aussi. +2: P – Jimbo

+10

Cette réponse est tellement d'argent. –

+1

Je voudrais suggérer deux ajouts supplémentaires. Tout d'abord, je vérifierais pour overflow/underflow sur l'expression 'Math.pow (10, d)' (au moins). Sur cette erreur ET lorsque decimalPlaces est positif, renvoyez num, sinon relancez cette exception. Deuxièmement, pour compenser les erreurs de conversion binaire IEEE, je changerais 'f == 0.5' pour quelque chose comme' f> = 0.499999999 && f <= 0.500000001' - en fonction de votre choix de 'epsilon' (je ne sais pas si .toFixed (epsilon) est assez). Alors tu es doré! – Marius

2

La réponse acceptée est arrondie à un nombre donné de places. Dans le processus, il appelle à Fixe qui convertit le nombre en une chaîne. Comme c'est cher, j'offre la solution ci-dessous. Il arrondit un nombre se terminant par 0,5 au nombre pair le plus proche. Il ne gère pas l'arrondi à un nombre arbitraire d'endroits.

function even_p(n){ 
    return (0===(n%2)); 
}; 

function bankers_round(x){ 
    var r = Math.round(x); 
    return (((((x>0)?x:(-x))%1)===0.5)?((even_p(r))?r:(r-1)):r); 
}; 
+0

votre fonction ne permet pas de spécifier le nombre de décimales à garder –

+0

True. C'est à gauche pour le redder :-). Lire: tout ce dont j'avais besoin était un arrondi correct à un entier. – soegaard

+0

C'est une excellente solution, merci! Voici comment vous le faites fonctionner pour les décimales. On dirait que je vais devoir poster une réponse séparée pour montrer le code ... – xims

2

C'est une excellente solution de @soegaard. Voici un petit changement qui fait fonctionner pour les points décimales:

bankers_round(n:number, d:number=0) { 
    var x = n * Math.pow(10, d); 
    var r = Math.round(x); 
    var br = (((((x>0)?x:(-x))%1)===0.5)?(((0===(r%2)))?r:(r-1)):r); 
    return br/Math.pow(10, d); 
} 

Et tout ce - voici quelques essais:

console.log(" 1.5 -> 2 : ", bankers_round(1.5)); 
console.log(" 2.5 -> 2 : ", bankers_round(2.5)); 
console.log(" 1.535 -> 1.54 : ", bankers_round(1.535, 2)); 
console.log(" 1.525 -> 1.52 : ", bankers_round(1.525, 2)); 

console.log(" 0.5 -> 0 : ", bankers_round(0.5)); 
console.log(" 1.5 -> 2 : ", bankers_round(1.5)); 
console.log(" 0.4 -> 0 : ", bankers_round(0.4)); 
console.log(" 0.6 -> 1 : ", bankers_round(0.6)); 
console.log(" 1.4 -> 1 : ", bankers_round(1.4)); 
console.log(" 1.6 -> 2 : ", bankers_round(1.6)); 

console.log(" 23.5 -> 24 : ", bankers_round(23.5)); 
console.log(" 24.5 -> 24 : ", bankers_round(24.5)); 
console.log(" -23.5 -> -24 : ", bankers_round(-23.5)); 
console.log(" -24.5 -> -24 : ", bankers_round(-24.5)); 
+0

Note: Certaines de ces conventions sont ES6 seulement (ex: valeurs parmales par défaut). – Spade

0

C'est le stackoverflow inhabituel où les réponses de fond sont mieux que l'accepté. Juste nettoyé la solution @xims et fait un peu plus lisible:

function bankersRound(n, d=2) { 
    var x = n * Math.pow(10, d); 
    var r = Math.round(x); 
    var br = Math.abs(x) % 1 === 0.5 ? (r % 2 === 0 ? r : r-1) : r; 
    return br/Math.pow(10, d); 
}