Усечение (не округление) десятичных чисел в javascript
Я пытаюсь усечь десятичные числа до десятичных знаков. Что-то вроде этого:
5.467 -> 5.46
985.943 -> 985.94
toFixed(2)
делает только о правильной вещи, но это округляет значение. Мне не нужно округлять значение. Надеюсь, что это возможно в JavaScript.
23 ответа:
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); }
ответ Догберта хорош, но если ваш код может иметь дело с отрицательными числами,
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)),
EDIT: shendz правильно указывает на то, что использование этого решения с
a = 17.56
будет неправильно производить17.55
. Подробнее о том, почему это происходит, читайте Что Каждый Компьютерный Ученый Должен Знать Об Арифметике С Плавающей Запятой. К сожалению, написание решения, которое устраняет все источники ошибок с плавающей запятой, довольно сложно с 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. Вы можете найти другие библиотеки javascript BigDecimal в этом вопросе SO: " есть ли хорошая библиотека Javascript BigDecimal?" и вот хороший пост в блоге о математические библиотеки для Javascript
считают пользуясь двойной Тильды:
~~
.взять в номер. Умножьте на значащие цифры после запятой, чтобы вы могли усечь до нуля мест с
~~
. Разделите этот множитель обратно. Прибыль.function truncator(numToTruncate, intDecimalPlaces) { var numPower = Math.pow(10, intDecimalPlaces); // "numPowerConverter" might be better return ~~(numToTruncate * numPower)/numPower; }
Я пытаюсь сопротивляться упаковка
~~
вызовите parens; порядок операций должен заставить это работать правильно, I верить.
alert(truncator(5.1231231, 1)); // is 5.1
alert(truncator(-5.73, 1)); // is -5.7
alert(truncator(-5.73, 0)); // is -5
EDIT: оглядываясь назад,я непреднамеренно также обрабатывал случаи, чтобы округлить левую часть десятичной дроби.
alert(truncator(4343.123, -2)); // gives 4300.
логика немного дурацкий ищет это использование, и может извлечь выгоду из быстрого рефакторинга. Но это все еще работает. Лучше повезло, чем хорошо.
Я думал, что брошу в ответ с помощью
|
так как это просто и хорошо работает.truncate = function(number, places) { var shift = Math.pow(10, places); return ((number * shift) | 0) / shift; };
хорошее однострочное решение:
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
@Dogbert ответ может быть улучшен с
Math.trunc
, который усекает вместо округления.есть разница между округлением и усечением. Усечение есть очевидно, что поведение, которое ищет этот вопрос. Если я позвоню усечь(-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
усечение с помощью побитовых операторов:
~~0.5 === 0 ~~(-0.5) === 0 ~~14.32794823 === 14 ~~(-439.93) === -439
тот, который помечен как решение, является лучшим решением, которое я нашел до сегодняшнего дня, но имеет серьезную проблему с 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); }
здесь простая, но рабочая функция усечения числа до 2 знаков после запятой.
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; }
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
Я нашел проблему: учитывая следующую ситуацию: 2.1 или 1.2 или -6.4
Что делать, если вы хотите всегда 3 десятичных знака или два или wharever, так что, вы должны заполнить ведущие нули справа
// 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
это фиксированная функция Ника Knowlson
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; };
результирующий тип остается числом...
/* Return the truncation of n wrt base */ var trunc = function(n, base) { n = (n / base) | 0; return base * n; }; var t = trunc(5.467, 0.01);
Я думаю, что эта функция может быть простое решение:
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));
вот мой взгляд на эту тему:
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)
просто чтобы указать на простое решение, которое сработало для меня
преобразовать его в строку, а затем регулярное выражение...
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])
вот что я использую:
var t = 1; for (var i = 0; i < decimalPrecision; i++) t = t * 10; var f = parseFloat(value); return (Math.floor(f * t)) / t;
вот код 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
Number.prototype.truncate = function(places) { var shift = Math.pow(10, places); return Math.trunc(this * shift) / shift; };
ответ @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
Скрипка: номер JS округляется
PS: недостаточно репо, чтобы прокомментировать это решение.
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
Lodash имеет несколько методов математической утилиты, которые могут круглые,пол и 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));