Как я могу округлить число в JavaScript? .toFixed () возвращает строку?

176

Я что-то здесь упускаю?

var someNumber = 123.456;
someNumber = someNumber.toFixed(2);
alert(typeof(someNumber));
//alerts string

Почему же.toFixed()возвращает строку?

Я хочу округлить число до 2 десятичных цифр.

Дерек Адэйр
источник
7
Потому что он предназначен для возврата строки?
Kennytm
2
Мне это кажется странным. .toFixed () работает только с числами ... верно?
Дерек Адэйр
10
Я понимаю, что Math.round () работает как положено. Я просто спрашивал, почему функция, которая работает с числами, возвращает строку ...
Дерек Адэйр
3
Люди, живущие в 2017 году, должны использовать такие библиотеки, как lodash.com/docs/4.17.4#ceil
Ив М.
1
Так же и _. рассчитывать? еще не повышен до своего брата.
Дженна Лиф

Ответы:

124

Он возвращает строку, потому что 0,1 и ее степени (которые используются для отображения десятичных дробей) не представимы (по крайней мере, не с полной точностью) в двоичных системах с плавающей запятой.

Например, 0,1 действительно 0,1000000000000000055511151231257827021181583404541015625, а 0,01 действительно 0,01000000000000000020816681711721685132943093776702880859375. (Спасибо BigDecimalза доказательство моей точки. :-P)

Поэтому (при отсутствии десятичного числа с плавающей запятой или типа рациональных чисел) вывод его в виде строки - единственный способ обрезать его с точностью, необходимой для отображения.

Крис Шут-Янг
источник
28
по крайней мере, javascript мог бы спасти меня от работы с пальцем и преобразовать ее обратно в число ... sheesh ...
Дерек Адэйр
10
@Derek: Да, но как только вы конвертируете его обратно в число, вы снова столкнетесь с теми же проблемами неточности. :-P JS не имеет десятичных чисел с плавающей точкой или рациональных чисел.
Крис Джестер-Янг
1
@DerekAdair Я недавно написал пост, который объясняет это еще больше, что может вас заинтересовать. Наслаждайтесь! stackoverflow.com/a/27030789/13
Крис Шестер-Янг
7
На самом деле это заставило меня провести довольно серьезные исследования по этому вопросу! Спасибо за всю твою помощь!
Дерек Адэйр
2
Ваш ответ немного вводит в заблуждение: toFixedэто функция форматирования, единственная цель которой - преобразовать число в строку и отформатировать его с использованием указанного числа десятичных знаков. Причина, по которой он возвращает строку, состоит в том, что он должен возвращать строку, и если бы она была названа toStringFixedвместо этого, OP не удивился бы результатам. Единственная проблема здесь в том, что OP ожидал, что он будет работать Math.round, не обращаясь к JS.
Groo
177

Number.prototype.toFixedэто функция, предназначенная для форматирования числа перед его печатью. Это из семьи toString, toExponentialи toPrecision.

Чтобы округлить число, вы должны сделать это:

someNumber = 42.008;
someNumber = Math.round( someNumber * 1e2 ) / 1e2;
someNumber === 42.01;

// if you need 3 digits, replace 1e2 with 1e3 etc.
// or just copypaste this function to your code:

function toFixedNumber(num, digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(num*pow) / pow;
}

,

Или, если вам нужна « родная » функция, вы можете расширить прототип:

Number.prototype.toFixedNumber = function(digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(this*pow) / pow;
}
someNumber = 42.008;
someNumber = someNumber.toFixedNumber(2);
someNumber === 42.01;


//or even hexadecimal

someNumber = 0xAF309/256  //which is af3.09
someNumber = someNumber.toFixedNumber(1, 16);
someNumber.toString(16) === "af3.1";

Однако имейте в виду, что загрязнение прототипа считается плохим, когда вы пишете модуль, поскольку модули не должны иметь побочных эффектов. Итак, для модуля используйте первую функцию .

m93a
источник
12
Я думаю, что это лучший ответ. Это позволяет избежать преобразования типов. Удивительный соус!
Фил
1
Отличный ответ! Однако ... Я занимался JavaScript около 20 лет или около того, но я не могу понять, почему вы используете эту + (...) конструкцию вокруг возвращаемого значения? Спасибо @sam за втирание :) Поскольку я никогда не слишком стар, чтобы учиться, пожалуйста, уточните :-)
HammerNL
1
@HammerNL Несмотря на убеждение Сэма, он на самом деле ничего не делает :) Это всего лишь практика - он заставляет IDE распознавать эту функцию как type Number. Дело в том, что +(anyValue)всегда возвращает число - например. +("45")возвращается 45, +(new Number(42))возвращается 42. Это похоже на строгую типизацию функции. Если вы привыкли к этому, вы можете избежать множества ошибок :)
m93a
Почему это не встроено в основной javascript: s
веб-мастер
2
Результата someNumber = Math.round( 42.008 * 1e2 ) / 1e2;нет 42.01, это так ~42.0099999999999980. Причина: число 42.01не существует и округляется до ближайшего существующего числа. Кстати, пробные номера, toPrecision(18)чтобы напечатать его со всеми соответствующими цифрами.
Wiimm
118

Я решил эту проблему, изменив это:

someNumber = someNumber.toFixed(2)

...к этому:

someNumber = +someNumber.toFixed(2);

Однако это преобразует число в строку и анализирует его снова, что окажет значительное влияние на производительность. Если вы заботитесь о производительности или безопасности типов, проверьте и другие ответы.

Ева Хуан
источник
39
Нет-нет-нет-нет-нет! Не делай этого! Преобразование числа в строку только в округление - очень плохая практика ! Вместо этого делай someNumber = Math.round(someNumber * 1e2) / 1e2! Смотрите мой ответ для более обобщенного способа.
m93a
@ m93a - почему такая плохая практика?
июня
3
@jczaplew Потому что, если вы сделаете это таким образом, 32-битное двоичное число преобразуется в строку, используя 16 бит для каждой чертовой десятичной цифры ! (Кстати, хранение чисел в UTF-16 - не самая удобная вещь, на которую вы пойдете.) И затем строка преобразуется обратно в 32-битное число с плавающей точкой. Цифра за цифрой. (Если я проигнорирую все тесты, которые должны быть сделаны прежде, чтобы выбрать правильный алгоритм синтаксического анализа.) И все это, напрасно учитывая, что вы можете сделать это, используя 3 быстрые операции с плавающей точкой.
m93a
2
@ m93a, так из соображений производительности? это на самом деле оказывает заметное влияние на производительность?
Себастьян
2
@jczaplew, потому что строки (1) практически медленные и (2) теоретически некорректны.
Pacerier
29

Почему бы не использовать parseFloat?

var someNumber = 123.456;
someNumber = parseFloat(someNumber.toFixed(2));
alert(typeof(someNumber));
//alerts number
sirlunchalot
источник
15

Я решил это с преобразованием обратно в число с помощью Number()функции JavaScript

var x = 2.2873424;
x = Number(x.toFixed(2));
Nizar
источник
12

Конечно, он возвращает строку. Если вы хотите округлить числовую переменную, используйте вместо этого Math.round (). Смысл toFixed состоит в том, чтобы отформатировать число с фиксированным числом десятичных знаков для отображения пользователю .

Джоэл Коухорн
источник
4

Вы можете просто использовать «+», чтобы преобразовать результат в число.

var x = 22.032423;
x = +x.toFixed(2); // x = 22.03
Мейсам
источник
3

Что вы ожидаете, что он вернется, когда он должен отформатировать число? Если у вас есть номер, вы не можете ничего с ним сделать, потому что, например, 2 == 2.0 == 2.00и т. Д., Так что это должна быть строка.

Томас Вана
источник
3

Чтобы привести пример того, почему это должна быть строка:

Если вы отформатируете 1.toFixed (2), вы получите «1,00».

Это не то же самое, что 1, поскольку 1 не имеет 2 десятичных знаков.


Я знаю, что JavaScript не совсем язык производительности , но есть вероятность, что вы получите более высокую производительность для округления, если вы используете что-то вроде: roundedValue = Math.round (value * 100) * 0.01

пирозапал
источник
2

Потому что его основное использование - отображение чисел? Если вы хотите округлить числа, используйте соответствующие Math.round()факторы.

Christoph
источник
но он отображает NUMBERS, поэтому он не должен возвращать «число»?
Дерек Адэйр
3
@Derek: Только так, '42'как это число ... что это не так. Тот факт, что строка содержит только цифры, не делает ее числом. Это не PHP. :-P
Крис Шестер-Янг
ржунимагу. Это не строка, содержащая число ... Это число, которое передается методу. Метод принимает число и возвращает строку.
Дерек Адэйр
@DerekAdair правильно, но браузер не может отобразить число, он отображает строки, таким образом, преобразование.
Ник М
1

Вот несколько более функциональный вариант ответа m93a.

const toFixedNumber = (toFixTo = 2, base = 10) => num => {
  const pow = Math.pow(base, toFixTo)
  return +(Math.round(num * pow) / pow)
}

const oneNumber = 10.12323223

const result1 = toFixedNumber(2)(oneNumber) // 10.12
const result2 = toFixedNumber(3)(oneNumber) // 10.123

// or using pipeline-operator
const result3 = oneNumber |> toFixedNumber(2) // 10.12
Sartaj
источник
это удобная функция, для неопределенного типа она не работает, я добавил код для этого случая; if (num! == undefined) {return + (Math.round (num * pow) / pow)} else {return 0; }
Дино Лю