Я пытаюсь усечь десятичные числа до десятичных знаков. Что-то вроде этого:
5.467 -> 5.46
985.943 -> 985.94
toFixed(2)
делает примерно то, что нужно, но округляет значение. Мне не нужно округлять значение. Надеюсь, это возможно в javascript.
Я пытаюсь усечь десятичные числа до десятичных знаков. Что-то вроде этого:
5.467 -> 5.46
985.943 -> 985.94
toFixed(2)
делает примерно то, что нужно, но округляет значение. Мне не нужно округлять значение. Надеюсь, это возможно в javascript.
Ответы:
UPD :
Итак, как выяснилось, ошибки округления всегда будут преследовать вас, как бы вы ни старались их компенсировать. Следовательно, проблему следует решать, представляя числа точно в десятичной системе счисления.
Number.prototype.toFixedDown = function(digits) {
var re = new RegExp("(\\d+\\.\\d{" + digits + "})(\\d)"),
m = this.toString().match(re);
return m ? parseFloat(m[1]) : this.valueOf();
};
[ 5.467.toFixedDown(2),
985.943.toFixedDown(2),
17.56.toFixedDown(2),
(0).toFixedDown(1),
1.11.toFixedDown(1) + 22];
// [5.46, 985.94, 17.56, 0, 23.1]
Старое подверженное ошибкам решение, основанное на компиляции чужих:
Number.prototype.toFixedDown = function(digits) {
var n = this - Math.pow(10, -digits)/2;
n += n / Math.pow(2, 53); // added 1360765523: 17.56.toFixedDown(2) === "17.56"
return n.toFixed(digits);
}
1.11.toFixedDown(1) + 22
заканчивается как 1.122
вместо 23.1
. Также 0.toFixedDown(1)
должен производить, 0
но вместо этого производит -0.1
.
(-10.2131).toFixedDown(2) // ==> 10.21
.
(1e-7).toFixedDown(0) // ==> 1e-7
. Есть ли что 1e-(>=7)
(например: 1e-8
, 1e-9
, ...).
Ответ Догберта хорош, но если ваш код может иметь дело с отрицательными числами, Math.floor
сам по себе может дать неожиданные результаты.
Например Math.floor(4.3) = 4
, ноMath.floor(-4.3) = -5
Вместо этого используйте вспомогательную функцию, подобную этой, чтобы получить согласованные результаты:
truncateDecimals = function (number) {
return Math[number < 0 ? 'ceil' : 'floor'](number);
};
// Applied to Dogbert's answer:
var a = 5.467;
var truncated = truncateDecimals(a * 100) / 100; // = 5.46
Вот более удобный вариант этой функции:
truncateDecimals = function (number, digits) {
var multiplier = Math.pow(10, digits),
adjustedNum = number * multiplier,
truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);
return truncatedNum / multiplier;
};
// Usage:
var a = 5.467;
var truncated = truncateDecimals(a, 2); // = 5.46
// Negative digits:
var b = 4235.24;
var truncated = truncateDecimals(b, -2); // = 4200
Если это нежелательное поведение, вставьте вызов Math.abs
в первую строку:
var multiplier = Math.pow(10, Math.abs(digits)),
РЕДАКТИРОВАТЬ: shendz правильно указывает, что использование этого решения a = 17.56
будет неправильно производить 17.55
. Чтобы узнать больше о том, почему это происходит, прочтите « Что должен знать каждый компьютерный ученый об арифметике с плавающей запятой». . К сожалению, написать решение, устраняющее все источники ошибок с плавающей запятой, с javascript довольно сложно. На другом языке вы бы использовали целые числа или, возможно, десятичный тип, но с javascript ...
Это решение должно быть на 100% точным, но оно будет и медленнее:
function truncateDecimals (num, digits) {
var numS = num.toString(),
decPos = numS.indexOf('.'),
substrLength = decPos == -1 ? numS.length : 1 + decPos + digits,
trimmedResult = numS.substr(0, substrLength),
finalResult = isNaN(trimmedResult) ? 0 : trimmedResult;
return parseFloat(finalResult);
}
Для тех, кому нужна скорость, но при этом нужно избегать ошибок с плавающей запятой, попробуйте что-нибудь вроде BigDecimal.js . Вы можете найти другие библиотеки BigDecimal для javascript в этом вопросе SO: «Есть ли хорошая библиотека BigDecimal для Javascript?» и вот хороший пост в блоге о математических библиотеках для Javascript
if(isNAN(result) result = 0;
Зависит от желаемого поведения.
var a = 5.467;
var truncated = Math.floor(a * 100) / 100; // = 5.46
truncate(-3.14)
и получу -4
ответ, я бы определенно назвал это нежелательным.
var a = 65.1
var truncated = Math.floor(a * 100) / 100; // = 65.09
Следовательно, это неправильное решение
Вы можете исправить округление, вычтя 0,5 для toFixed, например
(f - 0.005).toFixed(2)
Рассмотрим , воспользовавшись двойной тильдой:~~
.
Возьмите число. Умножьте на значащие цифры после запятой, чтобы можно было обрезать до нуля с помощью ~~
. Разделите множитель обратно. Прибыль.
function truncator(numToTruncate, intDecimalPlaces) {
var numPower = Math.pow(10, intDecimalPlaces); // "numPowerConverter" might be better
return ~~(numToTruncate * numPower)/numPower;
}
Я пытаюсь сопротивляться заключению ~~
вызова в скобки; Я считаю, что порядок операций должен заставить это работать правильно.
alert(truncator(5.1231231, 1)); // is 5.1
alert(truncator(-5.73, 1)); // is -5.7
alert(truncator(-5.73, 0)); // is -5
РЕДАКТИРОВАТЬ: Оглядываясь назад, я непреднамеренно также обрабатывал случаи, чтобы округлить левую часть десятичной дроби.
alert(truncator(4343.123, -2)); // gives 4300.
Логика выглядит немного странной для такого использования, и может быть полезно провести быстрый рефакторинг. Но все равно работает. Лучше повезет, чем хорошо.
Math
прототип этим и проверите NaN перед выполнением, это будет просто идеально.
truncator((10 * 2.9) / 100, 2)
вернуть 0,28 вместо 0,29 ... jsfiddle.net/25tgrzq1
Хорошее однострочное решение:
function truncate (num, places) {
return Math.trunc(num * Math.pow(10, places)) / Math.pow(10, places);
}
Затем вызовите его с помощью:
truncate(3.5636232, 2); // returns 3.56
truncate(5.4332312, 3); // returns 5.433
truncate(25.463214, 4); // returns 25.4632
Я подумал, что добавлю ответ, |
так как это просто и хорошо работает.
truncate = function(number, places) {
var shift = Math.pow(10, places);
return ((number * shift) | 0) / shift;
};
or
ing с 0 означает «просто сохраните то, что у меня уже есть». Делает то, что ~~
делает мой ответ, но с помощью одной побитовой операции. Хотя он имеет то же ограничение, что и написано: мы не можем перейти больше 2 ^ 31 .
truncate((10 * 2.9) / 100);
этот код возвращает 0,28 вместо 0,29 jsfiddle.net/9pf0732d
Обрезать с помощью побитовых операторов:
~~0.5 === 0
~~(-0.5) === 0
~~14.32794823 === 14
~~(-439.93) === -439
Ответ @Dogbert можно улучшить с помощью Math.trunc
, который усекает вместо округления.
Есть разница между округлением и усечением. Усечение явно является поведением, к которому стремится этот вопрос. Если я вызову truncate (-3.14) и получу -4 обратно, я определенно назвал бы это нежелательным. - @NickKnowlson
var a = 5.467;
var truncated = Math.trunc(a * 100) / 100; // = 5.46
var a = -5.467;
var truncated = Math.trunc(a * 100) / 100; // = -5.46
Math.trunc
, а чем 9.28 * 100
это , 927.9999
а не 928
. Возможно, вы захотите прочитать The Perils of Floating Point
Я написал ответ более коротким методом. Вот что я придумал
function truncate(value, precision) {
var step = Math.pow(10, precision || 0);
var temp = Math.trunc(step * value);
return temp / step;
}
Метод можно использовать так
truncate(132456.25456789, 5)); // Output: 132456.25456
truncate(132456.25456789, 3)); // Output: 132456.254
truncate(132456.25456789, 1)); // Output: 132456.2
truncate(132456.25456789)); // Output: 132456
Или, если вам нужен более короткий синтаксис, пожалуйста
function truncate(v, p) {
var s = Math.pow(10, p || 0);
return Math.trunc(s * v) / s;
}
Number.prototype.trim = function(decimals) {
var s = this.toString();
var d = s.split(".");
d[1] = d[1].substring(0, decimals);
return parseFloat(d.join("."));
}
console.log((5.676).trim(2)); //logs 5.67
Думаю, эта функция может быть простым решением:
function trunc(decimal,n=2){
let x = decimal + ''; // string
return x.lastIndexOf('.')>=0?parseFloat(x.substr(0,x.lastIndexOf('.')+(n+1))):decimal; // You can use indexOf() instead of lastIndexOf()
}
console.log(trunc(-241.31234,2));
console.log(trunc(241.312,5));
console.log(trunc(-241.233));
console.log(trunc(241.2,0));
console.log(trunc(241));
Я обнаружил проблему: учитывая следующую ситуацию: 2,1 или 1,2 или -6,4
Что делать, если вы хотите всегда 3 десятичных знака или два или что угодно, поэтому вам нужно заполнить ведущие нули справа
// 3 decimals numbers
0.5 => 0.500
// 6 decimals
0.1 => 0.10000
// 4 decimales
-2.1 => -2.1000
// truncate to 3 decimals
3.11568 => 3.115
Это фиксированная функция Ника Ноулсона.
function truncateDecimals (num, digits)
{
var numS = num.toString();
var decPos = numS.indexOf('.');
var substrLength = decPos == -1 ? numS.length : 1 + decPos + digits;
var trimmedResult = numS.substr(0, substrLength);
var finalResult = isNaN(trimmedResult) ? 0 : trimmedResult;
// adds leading zeros to the right
if (decPos != -1){
var s = trimmedResult+"";
decPos = s.indexOf('.');
var decLength = s.length - decPos;
while (decLength <= digits){
s = s + "0";
decPos = s.indexOf('.');
decLength = s.length - decPos;
substrLength = decPos == -1 ? s.length : 1 + decPos + digits;
};
finalResult = s;
}
return finalResult;
};
x = 0.0000
тест truncateDecimals (x, 2)
не пройден. возвращается 0
. не так, как ожидалось0.00
function toFixed(number, digits) {
var reg_ex = new RegExp("(\\d+\\.\\d{" + digits + "})(\\d)")
var array = number.toString().match(reg_ex);
return array ? parseFloat(array[1]) : number.valueOf()
}
var test = 10.123456789
var __fixed = toFixed(test, 6)
console.log(__fixed)
// => 10.123456
Ответ @kirilloid кажется правильным, однако основной код необходимо обновить. Его решение не заботится об отрицательных числах (которые кто-то упомянул в разделе комментариев, но не был обновлен в основном коде).
Обновление до полного окончательного протестированного решения:
Number.prototype.toFixedDown = function(digits) {
var re = new RegExp("([-]*\\d+\\.\\d{" + digits + "})(\\d)"),
m = this.toString().match(re);
return m ? parseFloat(m[1]) : this.valueOf();
};
Пример использования:
var x = 3.1415629;
Logger.log(x.toFixedDown(2)); //or use whatever you use to log
Fiddle: округление номера JS вниз
PS: Недостаточно репо, чтобы прокомментировать это решение.
Вот простая, но работающая функция для усечения числа до двух знаков после запятой.
function truncateNumber(num) {
var num1 = "";
var num2 = "";
var num1 = num.split('.')[0];
num2 = num.split('.')[1];
var decimalNum = num2.substring(0, 2);
var strNum = num1 +"."+ decimalNum;
var finalNum = parseFloat(strNum);
return finalNum;
}
Lodash имеет несколько вспомогательных методов Math , которые могут круглый , пол , и CEIL числа до заданной десятичной точности. Это оставляет нули в конце.
Они используют интересный подход, используя показатель степени числа. По-видимому, это позволяет избежать проблем с округлением.
(Примечание: func
есть Math.round
или ceil
или floor
в приведенном ниже коде)
// Shift with exponential notation to avoid floating-point issues.
var pair = (toString(number) + 'e').split('e'),
value = func(pair[0] + 'e' + (+pair[1] + precision));
pair = (toString(value) + 'e').split('e');
return +(pair[0] + 'e' + (+pair[1] - precision));
Вот мой взгляд на эту тему:
convert.truncate = function(value, decimals) {
decimals = (decimals === undefined ? 0 : decimals);
return parseFloat((value-(0.5/Math.pow(10, decimals))).toFixed(decimals),10);
};
Это просто немного более проработанная версия
(f - 0.005).toFixed(2)
То, что помечено как решение, является лучшим решением, которое я находил до сегодняшнего дня, но имеет серьезную проблему с 0 (например, 0.toFixedDown (2) дает -0.01). Поэтому я предлагаю использовать это:
Number.prototype.toFixedDown = function(digits) {
if(this == 0) {
return 0;
}
var n = this - Math.pow(10, -digits)/2;
n += n / Math.pow(2, 53); // added 1360765523: 17.56.toFixedDown(2) === "17.56"
return n.toFixed(digits);
}
const TO_FIXED_MAX = 100;
function truncate(number, decimalsPrecison) {
// make it a string with precision 1e-100
number = number.toFixed(TO_FIXED_MAX);
// chop off uneccessary digits
const dotIndex = number.indexOf('.');
number = number.substring(0, dotIndex + decimalsPrecison + 1);
// back to a number data type (app specific)
return Number.parseFloat(number);
}
// example
truncate(0.00000001999, 8);
0.00000001
работает с:
просто чтобы указать на простое решение, которое сработало для меня
преобразовать его в строку, а затем регулярное выражение ...
var number = 123.45678;
var number_s = '' + number;
var number_truncated_s = number_s.match(/\d*\.\d{4}/)[0]
var number_truncated = parseFloat(number_truncated_s)
Его можно сократить до
var number_truncated = parseFloat(('' + 123.4568908).match(/\d*\.\d{4}/)[0])
Вот код ES6, который делает то, что вы хотите
const truncateTo = (unRouned, nrOfDecimals = 2) => {
const parts = String(unRouned).split(".");
if (parts.length !== 2) {
// without any decimal part
return unRouned;
}
const newDecimals = parts[1].slice(0, nrOfDecimals),
newString = `${parts[0]}.${newDecimals}`;
return Number(newString);
};
// your examples
console.log(truncateTo(5.467)); // ---> 5.46
console.log(truncateTo(985.943)); // ---> 985.94
// other examples
console.log(truncateTo(5)); // ---> 5
console.log(truncateTo(-5)); // ---> -5
console.log(truncateTo(-985.943)); // ---> -985.94
Вы можете работать со струнами. Проверяет, есть ли '.' существует, а затем удаляет часть строки.
усечь (7,88, 1) -> 7,8
усечь (7,889, 2) -> 7,89
усечь (-7,88, 1) -> -7,88
function truncate(number, decimals) {
const tmp = number + '';
if (tmp.indexOf('.') > -1) {
return +tmp.substr(0 , tmp.indexOf('.') + decimals+1 );
} else {
return +number
}
}
Я немного сбит с толку, почему существует так много разных ответов на такой фундаментально простой вопрос; есть только два подхода, которые я видел, и которые мне показались достойными внимания. Я провел быстрый тест, чтобы увидеть разницу в скорости, используя https://jsbench.me/ .
Это решение, которое в настоящее время (26.09.2020) помечено как ответ:
function truncate(n, digits) {
var re = new RegExp("(\\d+\\.\\d{" + digits + "})(\\d)"),
m = n.toString().match(re);
return m ? parseFloat(m[1]) : n.valueOf();
};
[ truncate(5.467,2),
truncate(985.943,2),
truncate(17.56,2),
truncate(0, 1),
truncate(1.11, 1) + 22];
Однако здесь выполняются строковые и регулярные выражения, что обычно не очень эффективно, и есть функция Math.trunc, которая точно выполняет то, что хочет OP, только без десятичных знаков. Следовательно, вы можете легко использовать это плюс небольшую дополнительную арифметику, чтобы получить то же самое.
Вот еще одно решение, которое я нашел в этой теме, и я бы его использовал:
function truncate(n, digits) {
var step = Math.pow(10, digits || 0);
var temp = Math.trunc(step * n);
return temp / step;
}
[ truncate(5.467,2),
truncate(985.943,2),
truncate(17.56,2),
truncate(0, 1),
truncate(1.11, 1) + 22];
Первый метод "на 99,92% медленнее", чем второй, поэтому второй определенно один я рекомендовал бы использовать.
Хорошо, вернемся к поиску других способов избежать работы ...