implementazione interrotta con errori

L’implementazione predefinita di “Number.toFixed” di javascript sembra essere un po ‘rotta.

console.log((8.555).toFixed(2)); // returns 8.56 console.log((8.565).toFixed(2)); // returns 8.57 console.log((8.575).toFixed(2)); // returns 8.57 console.log((8.585).toFixed(2)); // returns 8.59 

Ho bisogno di un metodo di arrotondamento più coerente di quello.

Nell’intervallo compreso tra 8.500 e 8.660 i seguenti numeri non vengono arrotondati correttamente.

 8.575 8.635 8.645 8.655 

Ho cercato di correggere l’implementazione del prototipo come segue, ma è solo a metà strada. Qualcuno può suggerire qualche cambiamento che lo farebbe funzionare in modo più coerente?

 Number.prototype.toFixed = function(decimalPlaces) { var factor = Math.pow(10, decimalPlaces || 0); var v = (Math.round(this * factor) / factor).toString(); if (v.indexOf('.') >= 0) { return v + factor.toString().substr(v.length - v.indexOf('.')); } return v + '.' + factor.toString().substr(1); }; 

Questo a causa di errori in virgola mobile.

Confronta (8.575).toFixed(20) con (8.575).toFixed(3) e immagina questa proposizione: 8.575 < real("8.575") , dove real è una funzione immaginaria che crea un numero reale con precisione infinita.

Cioè, il numero originale non è come previsto e l'inesattezza è già stata introdotta.

Un rapido "workabout" a cui posso pensare è: Moltiplicare per 1000 (o come appropriato), ottenere il toFixed(0) di quello (ha ancora un limite, ma è assurdo), quindi tornare indietro nel formato decimale.

Buona programmazione.

Grazie per la risposta pst. La mia implementazione ha funzionato quasi, ma non in alcuni casi a causa di errori in virgola mobile.

questa linea nella mia funzione è il colpevole: Math.round (questo * fattore)

(è sul numero.prototype, quindi “questo” è il numero); 8.575 * 100 esce a 857.4999999999999, che a sua volta arrotonda. questo viene corretto cambiando la riga da leggere come segue: Math.round (Math.round (this * factor * 100) / 100)

La mia intera soluzione è ora cambiata in:

 Number.prototype.toFixed = function(decimalPlaces) { var factor = Math.pow(10, decimalPlaces || 0); var v = (Math.round(Math.round(this * factor * 100) / 100) / factor).toString(); if (v.indexOf('.') >= 0) { return v + factor.toString().substr(v.length - v.indexOf('.')); } return v + '.' + factor.toString().substr(1); }; 

È probabilmente correlato a problemi in virgola mobile, vedi Come gestire la precisione del numero in virgola mobile in JavaScript?

Forse aiuterà qualcuno, questa è la funzione popolare formatMoney (), ma con arrotondamenti corretti.

 Number.prototype.formatMoney = function() { var n = this, decPlaces = 2, decSeparator = ",", thouSeparator = " ", sign = n < 0 ? "-" : "", i = parseInt(n = Math.abs(+n || 0)) + "", j = (j = i.length) > 3 ? j % 3 : 0, decimals = Number(Math.round(n +'e'+ decPlaces) +'e-'+ decPlaces).toFixed(decPlaces), result = sign + (j ? i.substr(0, j) + thouSeparator : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thouSeparator) + (decPlaces ? decSeparator + Math.abs(decimals-i).toFixed(decPlaces).slice(2) : ""); return result; }; (9.245).formatMoney(); // returns 9,25 (7.5).formatMoney(); // returns 7,50 (8.575).formatMoney(); // returns 8,58 

Controlla la mia risposta

 function toFixed( num, precision ) { return (+(Math.round(+(num + 'e' + precision)) + 'e' + -precision)).toFixed(precision); }