Борисович Артем
10 сообщений
#16 лет назад
Здравствуйте!
Подскажите, есть ли в javascript функция для форматирования числа.
Например из $3000 вывести $3,000.00 ?

Пробовал искать нашел только как можно вывести 3000 > 3000.00 (Number(число).toFixed(2)).
Сергей В.
244 сообщения
#16 лет назад

function number_format (number, decimals, dec_point, thousands_sep)
{
var exponent = "";
var numberstr = number.toString ();
var eindex = numberstr.indexOf ("e");
if (eindex > -1)
{
exponent = numberstr.substring (eindex);
number = parseFloat (numberstr.substring (0, eindex));
}

if (decimals != null)
{
var temp = Math.pow (10, decimals);
number = Math.round (number * temp) / temp;
}
var sign = number < 0 ? "-" : "";
var integer = (number > 0 ?
Math.floor (number) : Math.abs (Math.ceil (number))).toString ();

var fractional = number.toString ().substring (integer.length + sign.length);
dec_point = dec_point != null ? dec_point : ".";
fractional = decimals != null && decimals > 0 || fractional.length > 1 ?
(dec_point + fractional.substring (1)) : "";
if (decimals != null && decimals > 0)
{
for (i = fractional.length - 1, z = decimals; i < z; ++i)
fractional += "0";
}

thousands_sep = (thousands_sep != dec_point || fractional.length == 0) ?
thousands_sep : null;
if (thousands_sep != null && thousands_sep != "")
{
for (i = integer.length - 3; i > 0; i -= 3)
integer = integer.substring (0 , i) + thousands_sep + integer.substring (i);
}

return sign + integer + fractional + exponent;
}
Константин Т.
589 сообщений
#16 лет назад
Попробуйте так :



<html>
<head>
<script type=text/javascript>

var str = "$3000000";
for(i=0; i<10;i++)str = str.replace(/(\d)(\d\d\d)(\.|\,|$)/, "$1,$2$3") ;
alert(str + '.00');

</script>

</head>
<body>
</body>
</html>



Ещё можно на ту же тему почитать
Вадим Т.
3240 сообщений
#16 лет назад
Вот, написал функцию для заданного форматирования числа, должно работать:
<html>
<body>
<script type=text/javascript>
function prepareNumber(number) {
var frac = Number(number).toFixed(2).split('.');
var aliq = frac.split('').reverse();
var result = aliq;
for (var i = 1; i < aliq.length; i++) {
if (i % 3 == 0) result += ',';
result += aliq;
}
return result.split('').reverse().join('') + '.' + frac;
}

alert(prepareNumber(3000000.2));
</script>
</body>
</html>

Кто сможет улучшить?
Вадим Т.
3240 сообщений
#16 лет назад
О, придумал более красивый вариант:
<html>
<body>
<script type=text/javascript>
function prepareNumber(number) {
var s = Number(number).toFixed(2);
var maxi = s.indexOf('.') - 1;
var result = s.substr(maxi);
for (var i = maxi - 1; i >= 0; i--) {
if ((maxi - i) % 3 == 0) result = ',' + result;
result = s.charAt(i) + result;
}
return result;
}

alert(prepareNumber(3000000.2));
</script>
</body>
</html>

Конечно, можно было бы сделать и универсальный вариант, если вынести в параметры функции или во внешние константы количество знаков после запятой, символ-десятичный разделитель, и символ-разделитель тысяч.
Константин Т.
589 сообщений
#16 лет назад
function prepareNumber2(number){
str = number+'';
for(i=0; i<10;i++)str = str.replace(/(\d)(\d\d\d)(\.|\,|$)/, "$1,$2$3") ;
return str.replace(/^(.*)\.(\d)$/, "$1\.$20");
}

alert(prepareNumber2(3000000.2));
Вадим Т.
3240 сообщений
#16 лет назад
Pilat66, идея с регекспом отличная. Хоть и был небольшой дефект в Вашем варианте, что не выводило бы нули после десятичной точки, если на вход приходит целое число (так было по условию задачи). Я немного доработал Вашу идею, плюс оптимизировал регексп и убрал лишние вызовы этого регекспа:
<html>
<body>
<script type="text/javascript">
function prepareNumber3(n) {
var s = Number(n).toFixed(2);
for (var i = Math.log(Math.abs(n)) / Math.LN10 / 3; i > 0; i--) s = s.replace(/(\d)(\d{3})/, "$1,$2");
return s;
}

alert(prepareNumber3(3000000.2));
</script>
</body>
</html>
Вадим Т.
3240 сообщений
#16 лет назад
А вот более красивый вариант. Ну, неужели еще проще можно придумать?
<html>
<body>
<script type="text/javascript">
function prepareNumber4(n){
var a, s = Number(n).toFixed(2);
while (a = s.match(/\d(\d{3})/)) s = s.replace(a, "," + a);
return s;
}

alert(prepareNumber4(-30000000.3));
</script>
</body>
</html>
Константин Т.
589 сообщений
#16 лет назад
Проще, наверно нельзя, но вот усовершенствовать на один регексп вроде удаётся:


<html>
<body>

<script type=text/javascript>

function prepareNumber4(n){
var a, s = Number(n).toFixed(2);
while (a = s.match(/\d(\d{3})/)) s = s.replace(a, "," + a);
return s;
}

function prepareNumber6(n){
var s = Number(n).toFixed(2) +'';
for(i=0; i < (s.length-3)/3 - 1; i++)s= s.replace(/(\d)(\d\d\d)(\.|\,|$)/, "$1,$2$3") ;
return s;
}

(new Array('', 0, 1, 1000000, 1000000.0, 1000000.00, 1000000. )).forEach(function(number){
document.write("<p>{5}\t'" + number + "'=" + "'" + prepareNumber6(number) + "'");
document.write("<br>{5}\t'" + - number + "'=" + "'" + prepareNumber6(-number) + "'");
});

</script>

</body>
</html>

Вадим Т.
3240 сообщений
#16 лет назад
Pilat66, таки да, Вы правы на регексп меньше получается. Только Ваш код можно немного улучшить, сейчас по пунктам покажу на примере prepareNumber6:

1. var s = Number(n).toFixed(2) +'';

Зачем Вы прибавляете в конце пустую строку? Возможно, для преобразования в строку? Но это делать избыточно, так как по спецификации toFixed() всегда возвращает именно строковое значение.

2. s= s.replace(/(-\d)(\d\d\d)(\.|\,|$)/, "$1,$2$3" ;

Я же написал выше этот же регексп но более компактно, оптимально и с меньшим количеством замен:
s = s.replace(/(\d)(\d{3})/, "$1,$2";
Плюс, тут еще то преимущество, что мы не привязываемся к конкретным символам "." и "," в теле регекспа, и можно было бы сделать универсальный вариант в будущем, когда параметрами можно было бы задавать эти делиметеры.

По сути, Ваш prepareNumber6 это то же самое что мой prepareNumber3, только Вы используете s.length в теле цикла (что оптимальнее, чем вычислять количество цифр в целой части с помощью логарифма, как это делал я, тут я с Вами согласен).

3. for(i=0; i < (s.length-3)/3 - 2; i++)

Обратите внимание, выражение (s.length-3)/3 - 1 вычисляется каждый раз при каждой итерации цикла, что избыточно, достаточно было бы сделать это один раз в начале.

4. Также Вы в prepareNumber6 не учитываете возможное отрицательное значение, из-за этого для чуть ли не трети всех отрицательных чисел выполняется лишний избыточный регексп.

Итак, совсестно мы с Вами родили наиболее оптимальный на данный момент вариант:
<html>
<body>
<script type="text/javascript">
function prepareNumber7(n){
var s = Number(n).toFixed(2);
for (var i = s.length / 3 - 2; i > 0; i--) s = s.replace(/(\d)(\d{3})/, "$1,$2");
return s;
}

// test
var arr = new Array(0, 1, 10, 100, 1000, 12345, 123456, 1234567, 12345678.91, 1234567890, 123456789012345.01);
for (var i = 0; i < arr.length; i++) {
var n = arr;
document.write(n + " = " + prepareNumber7(n) + " <font color=gray>" + -n + " = " + prepareNumber7(-n) + "</font><br>");
}
</script>
</body>
</html>

Ну что, будем пробовать добиться лучшего результата?
Вадим Т.
3240 сообщений
#16 лет назад
Сорри... все-таки все равно есть избыточность, даже в новом примере. В некоторых случаях регексп вызывается лишний раз. Сейчас подумаю еще. Главная проблема - в операциях с вещественными цислами (деление на 3), в некоторых случаях округляется не так как нужно, и получается избыточная итерация цикла. Сейчас попробую вообще избавиться от таких операций.
Вадим Т.
3240 сообщений
#16 лет назад
Ох ох... Я пришел к выводу что Number(n).toFixed(2); нельзя использовать, так как например 999.9999 будет преобразовано в 1000, а должно быть 999.99 (деньги обычно именно так нужно округлять).
Вадим Т.
3240 сообщений
#16 лет назад
Вот, триумф разума над черными силами!
Хотя, возможно, я опять что-то не учел, но этот вариант выглядит работающим правильно, и без лишних итераций. Плюс, удалось избавиться от медленных регекспов. Возможно еще можно пооптимизить... В общем, задачка оказалась далеко не так тривиальна, как можно было бы подумать вначале, целый ряд подводных камней.

<html>
<body>
<script type="text/javascript">
function prepareNumber8(n){
var s = Number(n).toFixed(12);
s = s.substr(0, s.length - 10);
for (var i = s.length - 6, j = n < 0 ? 1 : 0; i > j; i -= 3) s = s.substr(0, i) + "," + s.substr(i);
return s;
}

// test
var arr = new Array(0, 1, 9, 10, 99, 100, 999.9999, 1000, 10000, 100000, 1000000, 10000000.91, 1234567890.01);
for (var i = 0; i < arr.length; i++) {
var n = arr;
document.write(n + " = " + prepareNumber8(n) + " <font color=gray>" + -n + " = " + prepareNumber8(-n) + "</font><br>");
}
</script>
</body>
</html>


=== UPD ===
Тут некоторые спрашивают откуда взялись magic numbers 12, 10, и 6?
Очень просто.
12 = максимальная погрешность, которую принял в этом примере для вещественных чисел, можно это значение было бы вынести в константы. например, 0.00000000000000001 при этом будет считаться нулем.
10 = 12 - 2
6 = 2 + 1 + 3
Константин Т.
589 сообщений
#16 лет назад
Есть ещё потенциальная засада: toFixed имеет не только ограничение в 21 знак, но и глючок в 14-значных числах, причём 21 знак - не ограничение для IE.

<html>
<body>
<script type="text/javascript">
function prepareNumber8(n){
var s = Number(n).toFixed(12);
s = s.substr(0, s.length - 10);
for (var i = s.length - 6, j = n < 0 ? 1 : 0; i > j; i -= 3) s = s.substr(0, i) + "," + s.substr(i);
return s;
}

// test
x='';
for(k=1; k<23; k++){
x = x + '9';
n=(x + '.99');
document.write('#' + k + "\t" + n + " = " + prepareNumber8(n) + " <font color=gray>" + -n + " = " + prepareNumber8(-n) + "</font><br>");
}
</script>
</body>
</html>
Вадим Т.
3240 сообщений
#16 лет назад
Таки да, значит, отказываемся вообще от toFixed. Сейчас подумаю какой реализацией ее можно было бы заменить.
Вадим Т.
3240 сообщений
#16 лет назад
Вот пример, уже не такой изящный, зато лишен недостатков предыдущий версий. По сути, тут мы уже не работаем с числами, а только со строками.
<html>
<body>
<script type="text/javascript">
function prepareNumber8(n){
var s = String(n);
var k = s.indexOf(".");
if (k < 0) {
k = s.length;
s += ".00";
} else {
s += "00"
}
s = s.substr(0, k + 3);
for (var i = k - 3, j = n < 0 ? 1 : 0; i > j; i -= 3) s = s.substr(0, i) + "," + s.substr(i);
return s;
}

// test 1
for (var k = 1, x = "9"; k < 23; k++, x += "9") {
var n = x + ".99";
document.write("#" + k + "\t" + n + " = " + prepareNumber8(n) + " <font color=gray>-" + n + " = " + prepareNumber8("-" + n) + "</font><br>");
}

// test 2
var arr = new Array(0, 1, 9, 10/3, 99, 100, 999.9999, 1000, 10000, 100000, 1000000, 10000000.91, 1234567890.01);
for (var i = 0; i < arr.length; i++) {
var n = arr;
document.write(n + " = " + prepareNumber8(n) + " <font color=gray>-" + n + " = " + prepareNumber8("-" + n) + "</font><br>");
}
</script>
</body>
</html>