Округлете до максимум 2 знаків після коми (лише за потреби)


2756

Я хотів би округнути максимум 2 десяткових знаки, але лише за потреби .

Вхід:

10
1.7777777
9.1

Вихід:

10
1.78
9.1

Як я можу це зробити в JavaScript?


22
Я зробив скрипку з багатьма з методів , пропонованих в якості рішень тут ... так що ви можете порівняти: скрипку
dsdsdsdsd

37
Здається, ніхто цього не знає Number.EPSILON. Використовуйте Math.round( num * 100 + Number.EPSILON ) / 100.
cronvel

3
Для нових читачів цього не можна зробити, якщо у вас не буде результату типу рядка . Математика з плаваючою комою на двійкових внутрішніх поданнях чисел означає, що завжди є числа, які не можуть бути представлені як акуратні десяткові числа.
Walf

9
@cronvel Чи можете ви пояснити причину використання Number.EPSILONтут?
Брюс Нд

5
Я впав у кролячу нору і перевірив деякі цікавіші відповіді на цій сторінці (лише перша сторінка). Ось Кодейн . Підказка: чим більше обґрунтованих відповідей, тим нижчі шанси на те, що вона працює належним чином.
Адам Ягош

Відповіді:


3489

Використовуйте Math.round(num * 100) / 100

Редагувати: для забезпечення правильних речей, таких як 1.005, ми використовуємо

Math.round((num + Number.EPSILON) * 100) / 100


395
Хоча це буде працювати в більшості випадків, це не буде працювати для 1.005, що в кінцевому підсумку вийде 1, а не 1,01
Джеймс

83
@James Нічого собі, це дійсно дивно - я працюю в консолі розробника Chrome, і помічаю, що 1.005 * 100 = 100.49999999999999. Math.round (100.49999999999999) оцінює до 100, тоді як Math.round (100.5) - 101. IE9 робить те ж саме. Це пов'язано з
дивацтвом

153
Просте вирішення. Для 2 dp використовуйте Math.round((num + 0.00001) * 100) / 100. Спробуйте Math.round((1.005 + 0.00001) * 100) / 100іMath.round((1.0049 + 0.00001) * 100) / 100
mrkschan

31
@mrkschan Чому це працює, і чи це безглуздо для всіх чисел?
CMCDragonkai

86
Для тих із вас, хто цього не сприймає, ця методика називається масштабуванням. В основному, відповідь тут полягає в тому, щоб перенести дві цифри через десяткову точку, перетворивши фігуру на ціле число, щоб уникнути всіх божевільних питань з плаваючою комою, округлити це, а потім перевести його назад у те, що було раніше, діливши на 100, і у вас є відповідь на 2dp.
Alex_Nabu

3061

Якщо значення є текстовим типом:

parseFloat("123.456").toFixed(2);

Якщо значення є числом:

var numb = 123.23454;
numb = numb.toFixed(2);

Існує і зворотний бік, що значення на зразок 1,5 дають "1,50" як вихід. Виправлення, запропоноване @minitech:

var numb = 1.5;
numb = +numb.toFixed(2);
// Note the plus sign that drops any "extra" zeroes at the end.
// It changes the result (which is a string) into a number again (think "0 + foo"),
// which means that it uses only as many digits as necessary.

Здається Math.round, це краще рішення. Але це не так! У деяких випадках НЕ буде правильно округлятися:

Math.round(1.005 * 1000)/1000 // Returns 1 instead of expected 1.01!

toFixed () також НЕ буде правильно округлятися в деяких випадках (перевірено в Chrome v.55.0.2883.87)!

Приклади:

parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.
parseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56.
// However, it will return correct result if you round 1.5551.
parseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.

1.3555.toFixed(3) // Returns 1.355 instead of expected 1.356.
// However, it will return correct result if you round 1.35551.
1.35551.toFixed(2); // Returns 1.36 as expected.

Я думаю, це тому, що 1.555 насправді є щось на кшталт плаваючого 1.55499994 за лаштунками.

Рішення 1 - використовувати сценарій з необхідним алгоритмом округлення, наприклад:

function roundNumber(num, scale) {
  if(!("" + num).includes("e")) {
    return +(Math.round(num + "e+" + scale)  + "e-" + scale);
  } else {
    var arr = ("" + num).split("e");
    var sig = ""
    if(+arr[1] + scale > 0) {
      sig = "+";
    }
    return +(Math.round(+arr[0] + "e" + sig + (+arr[1] + scale)) + "e-" + scale);
  }
}

https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview

ПРИМІТКА. Це не універсальне рішення для всіх. Існує кілька різних алгоритмів округлення, ваша реалізація може бути різною, залежить від ваших вимог. https://en.wikipedia.org/wiki/Rounding

Рішення 2 полягає в тому, щоб уникнути обчислень на передньому кінці та витягнути округлені значення з сервера, що веде спокій.


81
Цей підхід (toFixed) є хорошим і працював для мене, але він конкретно не відповідає первісному запиту "лише за потреби". (Він кругообіг 1,5-1,50, що порушує специфікацію)
Пер Лундберг,

29
Для вимоги "при необхідності" зробіть це: parseFloat(number.toFixed(decimalPlaces)); @PerLundberg
Onur Yıldırım

36
parseFloat("55.555").toFixed(2)повертається "55.55"в консолі розробника Chrome.
Леві Ботелхо

22
Немає переваги використовувати toFixed замість Math.round; toFixed призводить до таких самих проблем із округленням (спробуйте їх із 5.555 та 1.005), але наче 500 разів (не жартуючи) повільніше, ніж Math.round ... Схоже, відповідь @MarkG тут точніша.
П’єр

17
toFixed не «іноді» повертає рядок, вона завжди повертає рядок.
McGuireV10,

464

Можна використовувати

function roundToTwo(num) {    
    return +(Math.round(num + "e+2")  + "e-2");
}

Я знайшов це на MDN . Їх шлях дозволяє уникнути проблеми, про яку згадувалося 1.005 .

roundToTwo(1.005)
1.01
roundToTwo(10)
10
roundToTwo(1.7777777)
1.78
roundToTwo(9.1)
9.1
roundToTwo(1234.5678)
1234.57

13
@Redsandro +(val)- еквівалент примусу використання Number(val). Об’єднання "e-2" у число призвело до рядка, який потрібно було перетворити назад у число.
Джек

41
Слідкуйте за тим, щоб великі та крихітні поплавці, які створювали б NaN, наприклад, наприклад, "1e-21 + 2" не будуть правильно проаналізовані.
П’єр

16
Ви 'вирішили' проблему 1.005, але ввели нову: тепер на консолі Chrome roundToTwo(1.0049999999999999)виходить як 1,01 (неминуче, оскільки 1.0049999999999999 == 1.005). Мені здається, що поплавок, який ви отримаєте, якщо введете num = 1.005"очевидно" ", повинен" округнути до 1,00, оскільки точне значення числа менше 1,005. Звичайно, мені також здається, що рядок '1.005' 'очевидно' 'повинен бути округлений до 1,01. Той факт, що різні люди, здається, мають різні інтуїції щодо того, яка тут реальна правильна поведінка, є частиною того, чому це складно.
Марк Амері

35
Там немає ( з плаваючою точкою) числа між ними 1.0049999999999999і 1.005, таким чином , за визначенням, вони таким же числом. Це називається дедекіндовим розрізом.
Азмісов

6
@Azmisov має рацію. Поки 1.00499 < 1.005є true, 1.0049999999999999 < 1.005оцінює до false.
falconepl

146

Відповідь MarkG правильна. Ось загальне розширення для будь-якої кількості десяткових знаків.

Number.prototype.round = function(places) {
  return +(Math.round(this + "e+" + places)  + "e-" + places);
}

Використання:

var n = 1.7777;    
n.round(2); // 1.78

Тест одиниці:

it.only('should round floats to 2 places', function() {

  var cases = [
    { n: 10,      e: 10,    p:2 },
    { n: 1.7777,  e: 1.78,  p:2 },
    { n: 1.005,   e: 1.01,  p:2 },
    { n: 1.005,   e: 1,     p:0 },
    { n: 1.77777, e: 1.8,   p:1 }
  ]

  cases.forEach(function(testCase) {
    var r = testCase.n.round(testCase.p);
    assert.equal(r, testCase.e, 'didn\'t get right number');
  });
})

20
П’єр підняв серйозне питання з відповіддю МаркГ.
dsjoerg

9
Примітка: Якщо ви не хочете змінювати прототип Number.proto, просто напишіть це як функцію: function round(number, decimals) { return +(Math.round(number + "e+" + decimals) + "e-" + decimals); }
Philipp Tsipman

2
Класний спосіб розширити це. Ви можете перевірити, чи не десяткова є негативною, тоді переверніть e + і e-. Тоді n = 115; n.round (-2); вироблять 100
Лі Лув'є

3
Спробуйте n: 1e + 19 - він повертається NaN
DavidJ

3
Цей алгоритм завжди закріплюється вгору (замість від нуля). Тож у випадку від’ємних чисел результат не такий, який ви могли очікувати:(-1.005).round(2) === -1
Олексій Комаров

123

Ви повинні використовувати:

Math.round( num * 100 + Number.EPSILON ) / 100

Здається, ніхто цього не знає Number.EPSILON.

Також варто зазначити, що це не дивина JavaScript, як це заявляли деякі люди.

Це просто так, як цифри з плаваючою комою працюють в комп'ютері. Як і 99% мов програмування, у JavaScript немає домашніх цифр з плаваючою комою; для цього вона покладається на CPU / FPU. Комп'ютер використовує двійкові, а в двійковій не існує таких чисел 0.1, а просто двійкове наближення до цього. Чому? З тієї ж причини, що 1/3 не можна записати у десятковій формі: її значення 0,33333333 ... з нескінченністю трійки.

Ось приходьте Number.EPSILON. Це число є різницею між 1 та наступним числом, що існує у числах з плаваючою комою з подвійною точністю. Ось так: Немає числа між 1та 1 + Number.EPSILON.

Редагувати:

Запитано в коментарях, давайте уточнимо одне: додавання Number.EPSILONдоречне лише тоді, коли значення для округлення є результатом арифметичної операції, оскільки воно може проковтнути деяку дельту помилки з плаваючою комою.

Це не корисно, коли значення надходить з прямого джерела (наприклад: буквальний, вхід користувача або датчик).

EDIT (2019):

Як і @maganap, і деякі народи вказали, найкраще додати, Number.EPSILONперш ніж помножувати:

Math.round( ( num + Number.EPSILON ) * 100 ) / 100

EDIT (грудень 2019):

Останнім часом я використовую функцію, подібну до цієї, для порівняння цифр, відомих epsilon:

const ESPILON_RATE = 1 + Number.EPSILON ;
const ESPILON_ZERO = Number.MIN_VALUE ;

function epsilonEquals( a , b ) {
  if ( Number.isNaN( a ) || Number.isNaN( b ) ) {
    return false ;
  }
  if ( a === 0 || b === 0 ) {
    return a <= b + EPSILON_ZERO && b <= a + EPSILON_ZERO ;
  }
  return a <= b * EPSILON_RATE && b <= a * EPSILON_RATE ;
}

Мій приклад використання - це констатація + перевірка даних, яку я розробляю протягом багатьох років.

Насправді, в коді, який я використовую, ESPILON_RATE = 1 + 4 * Number.EPSILONі EPSILON_ZERO = 4 * Number.MIN_VALUE(чотири рази епсілон), тому що я хочу, щоб перевірка рівності була досить вільною для накопичення помилки з плаваючою комою.

Поки що це виглядає ідеально для мене. Сподіваюся, це допоможе.


1
@palota, ви могли б визначити дійсно маленьке число, як згадує cronvel EPSILON
Даніель Сан

3
Це правильна відповідь! Сама проблема пов'язана з тим, як плаваючі номери працюють всередині, а не про javascript
Daniel San,

22
Насправді ви повинні додати епсилон ПЕРЕД множенням на 100 Math.round( (num + Number.EPSILON) * 100) / 100. Я погоджуюся також, що це правильний спосіб округлення правильно (хоча це не саме те, що було задано в цьому питанні).
maganap

2
@cronvel Хм. Ти правий; вибір напрямку має сенс. Тож я здогадуюсь, що моє залишилося заперечення - це лише те, що робити це має сенс, якщо ви працюєте в домені, де у вас є принципова причина думати, що ваше значення - це "кругле число". Якщо ви знаєте, що ваше введення є результатом простих арифметичних операцій над числами з невеликою кількістю десяткових знаків, то впевнені, що ви, ймовірно, маєте лише 0.004999999999999999в результаті складеної помилки з плаваючою комою, а математично правильний результат був, ймовірно, 0,005. Якщо це зчитування з датчика? Не так багато.
Марк Амері

1
@marchaos Він не підведе: половинки завжди округляється вгору . Напр. Math.round(1.5)= 2, але Math.round(-1.5)= -1. Тож це цілком відповідає. Тут -1 більше -2, так само як -1000 більше, ніж -1000.01. Не плутати з більшими абсолютними числами.
cronvel

84

Можна використовувати .toFixed(NumberOfDecimalPlaces).

var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23

4
Також тому, що він додає нульові нулі, що не те, про що було задано первісне питання .
Аластер Мау

2
Але кінцеві нулі легко знімаються за допомогою регулярного вираження, тобто `Number (10.10000.toFixed (2) .replace (/ 0 + $ /, ''))` => 10.1
Чад

1
@daniel верхня відповідь така ж (як і зараз), але вона не завжди округляється правильно, спробуйте +(1.005).toFixed(2)повернути 1замість 1.01.
Еміль Бержерон

3
@ChadMcElligott: Ваш регулярний вираз не працює з цілими числами: Number(9).toFixed(2).replace(/0+$/, '')=> "9."
Якоб ван Лінген

Здається, це не кругло. Це в основному усікання. У деяких випадках досить добре! Дякую.
iedmrc

78

Це питання є складним.

Припустимо, у нас є функція, roundTo2DP(num)яка приймає float як аргумент і повертає значення, округлене до двох десяткових знаків. До чого повинен оцінювати кожен із цих виразів?

  • roundTo2DP(0.014999999999999999)
  • roundTo2DP(0.0150000000000000001)
  • roundTo2DP(0.015)

Очевидна відповідь полягає в тому, що перший приклад повинен закруглюватися до 0,01 (тому що це ближче до 0,01, ніж до 0,02), а інші два повинні округлювати до 0,02 (тому що 0,0150000000000000001 ближче до 0,02, ніж до 0,01, а тому, що 0,015 знаходиться точно на півдорозі між їх і існує математична умова, що такі числа округляються).

Улов, про який ви, можливо, здогадалися, полягає в тому, що roundTo2DP неможливо здійснити такі очевидні відповіді, оскільки всі три числа, передані йому, є однаковим числом . Двоєчні номери з плаваючою комою IEEE 754 (вид, який використовується JavaScript) не можуть точно представляти більшість не цілих чисел, і тому всі три числові літерали вище округляються до дійсного номера з плаваючою точкою поблизу. Це число, як це відбувається, саме так

0.01499999999999999944488848768742172978818416595458984375

що ближче до 0,01, ніж до 0,02.

Ви можете бачити, що всі три номери однакові на консолі браузера, оболонці вузла чи іншому інтерпретаторі JavaScript. Просто порівняйте їх:

> 0.014999999999999999 === 0.0150000000000000001
true

Тож коли я пишу m = 0.0150000000000000001, точне значення, вm якому я закінчуюсь, ближче, 0.01ніж це 0.02. І все-таки, якщо я переходжу mна String ...

> var m = 0.0150000000000000001;
> console.log(String(m));
0.015
> var m = 0.014999999999999999;
> console.log(String(m));
0.015

... Я отримую 0,015, що повинно закруглюватися до 0,02, і це помітно не 56-десяткове місце, про яке я раніше говорив, що всі ці числа були точно рівними. То яка це темна магія?

Відповідь можна знайти в специфікації ECMAScript, в розділі 7.1.12.1: ToString, застосований до типу Number . Тут викладені правила перетворення деякого числа m у рядок. Ключова частина - точка 5, в якій генерується ціле число , чиї цифри будуть використані в рядковому поданні m :

нехай n , k і s будуть цілими числами такі, що k ≥ 1, 10 k -1s <10 k , значення числа для s × 10 n - k дорівнює m , а k - якомога менше. Зауважимо, що k - кількість цифр у десятковому поданні s , що s не ділиться на 10, і що найменша значуща цифра s не обов'язково однозначно визначається цими критеріями.

Ключова частина тут - вимога, щоб " k було якомога менше". Зазначена вимога - це вимога, згідно з якою число m, значення " String(m)must" має якнайменше можливу кількість цифр , все ще задовольняючи вимогу, яка Number(String(m)) === m. Оскільки ми це вже знаємо 0.015 === 0.0150000000000000001, тепер зрозуміло, чому String(0.0150000000000000001) === '0.015'має бути правдою.

Звичайно, жодна з цієї дискусії прямо не відповіла, що roundTo2DP(m) слід повернути. Якщо mточне значення дорівнює 0,01499999999999999944488848768742172978818416595458984375, але його рядкове представлення дорівнює "0,015", то яка правильна відповідь - математично, практично, філософськи чи будь-що інше - коли ми округляємо його до двох десяткових знаків?

На це немає єдиної правильної відповіді. Це залежить від вашого випадку використання. Ви, мабуть, хочете поважати представлення String і крутити вгору, коли:

  • Представлене значення по суті є дискретним, наприклад, кількість валюти в валюті 3-значного десяткового місця, як динари. У цьому випадку справжнє значення числа типу 0,015 дорівнює 0,015, а подання 0,0149999999 ..., яке воно отримує у двійковій плаваючій точці, є помилкою округлення. (Звичайно, багато хто з розумом стверджує, що ви повинні використовувати десяткову бібліотеку для обробки таких значень і ніколи не представляти їх як двійкові числа з плаваючою точкою в першу чергу.)
  • Значення було введено користувачем. У цьому випадку, знову ж таки, введене точне десяткове число є більш "істинним", ніж найближче двійкове подання з плаваючою комою.

З іншого боку, ви, мабуть, хочете поважати значення бінарної плаваючої точки і округлювати його вниз, коли ваше значення є за властивою безперервною шкалою - наприклад, якщо це зчитування з датчика.

Ці два підходи вимагають різного коду. Для поваги рядкового представлення числа, ми можемо (маючи досить трохи тонкий код) реалізувати власне округлення, яке діє безпосередньо на представлення рядка, цифру за цифрою, використовуючи той самий алгоритм, який ви використовували б у школі, коли ви вчили як округляти числа. Нижче наводиться приклад, який дотримується вимоги ОП щодо представлення числа до двох десяткових знаків "лише у разі необхідності", знімаючи проміжні нулі після десяткової крапки; можливо, вам, можливо, доведеться підлаштувати його до ваших точних потреб.

/**
 * Converts num to a decimal string (if it isn't one already) and then rounds it
 * to at most dp decimal places.
 *
 * For explanation of why you'd want to perform rounding operations on a String
 * rather than a Number, see http://stackoverflow.com/a/38676273/1709587
 *
 * @param {(number|string)} num
 * @param {number} dp
 * @return {string}
 */
function roundStringNumberWithoutTrailingZeroes (num, dp) {
    if (arguments.length != 2) throw new Error("2 arguments required");

    num = String(num);
    if (num.indexOf('e+') != -1) {
        // Can't round numbers this large because their string representation
        // contains an exponent, like 9.99e+37
        throw new Error("num too large");
    }
    if (num.indexOf('.') == -1) {
        // Nothing to do
        return num;
    }

    var parts = num.split('.'),
        beforePoint = parts[0],
        afterPoint = parts[1],
        shouldRoundUp = afterPoint[dp] >= 5,
        finalNumber;

    afterPoint = afterPoint.slice(0, dp);
    if (!shouldRoundUp) {
        finalNumber = beforePoint + '.' + afterPoint;
    } else if (/^9+$/.test(afterPoint)) {
        // If we need to round up a number like 1.9999, increment the integer
        // before the decimal point and discard the fractional part.
        finalNumber = Number(beforePoint)+1;
    } else {
        // Starting from the last digit, increment digits until we find one
        // that is not 9, then stop
        var i = dp-1;
        while (true) {
            if (afterPoint[i] == '9') {
                afterPoint = afterPoint.substr(0, i) +
                             '0' +
                             afterPoint.substr(i+1);
                i--;
            } else {
                afterPoint = afterPoint.substr(0, i) +
                             (Number(afterPoint[i]) + 1) +
                             afterPoint.substr(i+1);
                break;
            }
        }

        finalNumber = beforePoint + '.' + afterPoint;
    }

    // Remove trailing zeroes from fractional part before returning
    return finalNumber.replace(/0+$/, '')
}

Приклад використання:

> roundStringNumberWithoutTrailingZeroes(1.6, 2)
'1.6'
> roundStringNumberWithoutTrailingZeroes(10000, 2)
'10000'
> roundStringNumberWithoutTrailingZeroes(0.015, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.015000', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(1, 1)
'1'
> roundStringNumberWithoutTrailingZeroes('0.015', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(0.01499999999999999944488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.01499999999999999944488848768742172978818416595458984375', 2)
'0.01'

Наведена вище функція - це, мабуть, те, що ви хочете використати, щоб уникнути того, що користувачі ніколи не засвідчують числа, що вони неправильно округлили.

(Як альтернатива, ви також можете спробувати бібліотеку round10, яка забезпечує функцію, що має аналогічну поведінку, з дивовижною реалізацією.)

Але що робити, якщо у вас є другий вид Числа - значення, узяте з безперервної шкали, де немає підстав вважати, що приблизні десяткові подання з меншою кількістю десяткових знаків є більш точними, ніж ті, що мають більше? У цьому випадку ми не хочемо поважати представлення String, тому що це представлення (як пояснено в специфікації) вже начебто закруглене; ми не хочемо помилятися, кажучи "0,014999999 ... 375 раундів до 0,015, що до 0,02, так що 0,014999999 ... 375, до 0,02".

Тут ми можемо просто використовувати вбудований toFixedметод. Зауважте, що зателефонувавши Number()до рядка, який повернувся toFixed, ми отримаємо число, у якого в String-репрезентації немає нульових нулів (завдяки тому, як JavaScript обчислює рядкове представлення числа, обговорене раніше у цій відповіді).

/**
 * Takes a float and rounds it to at most dp decimal places. For example
 *
 *     roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
 *
 * returns 1.234
 *
 * Note that since this treats the value passed to it as a floating point
 * number, it will have counterintuitive results in some cases. For instance,
 * 
 *     roundFloatNumberWithoutTrailingZeroes(0.015, 2)
 *
 * gives 0.01 where 0.02 might be expected. For an explanation of why, see
 * http://stackoverflow.com/a/38676273/1709587. You may want to consider using the
 * roundStringNumberWithoutTrailingZeroes function there instead.
 *
 * @param {number} num
 * @param {number} dp
 * @return {number}
 */
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
    var numToFixedDp = Number(num).toFixed(dp);
    return Number(numToFixedDp);
}

У деяких крайніх випадках це не працює: спробуйте ( jsfiddle ) з roundStringNumberWithoutTrailingZeroes(362.42499999999995, 2). Очікуваний результат (як в PHP echo round(362.42499999999995, 2)) 362.43. Фактичний результат:362.42
Д-р Джанлуїджі Зене Занеттіні

1
@ Dr.GianluigiZaneZanettini Huh. Дивно. Я не впевнений, чому PHP roundдає 362,43. Це здається інтуїтивно неправильним, оскільки 362.42499999999995 менше 362.425 (в математиці та коді - 362.42499999999995 < 362.425вірно і в JS, і в PHP). Він також не відповідь РНР мінімізувати відстань між вихідними і закругленими числами з плаваючою точкою, так як 362.43 - 362.42499999999995 > 362.42499999999995 - 362.42. Відповідно до php.net/manual/en/function.round.php , PHP roundвідповідає стандарту C99; Мені доведеться ризикувати землею С, щоб зрозуміти, що відбувається.
Марк Амері

1
Переглянувши реалізацію округлення у джерелі PHP , я не маю жодної гадки про те, що це робить. Це жахливо складна реалізація, наповнена макросами та гілками та перетвореннями рядків та розгалуженнями на жорстко закодовані значення магічної точності. Я не впевнений, що сказати за межами "Відповідь PHP очевидно неправильна, і ми повинні подати звіт про помилку". Як ти до речі знайшов номер 362.42499999999995?
Марк Амері

1
@ Dr.GianluigiZaneZanettini Я створив звіт про помилку: bugs.php.net/bug.php?id=75644
Марк

2
@ Dr.GianluigiZaneZanettini "Я маю сенс?" - ні; це не так, як працює округлення. 1.000005 закінчується на п’ять, але якщо округнути до найближчого цілого числа, відповідь має бути 1, а не 2. Аналогічно, 1499995 закінчується п’ятьма, але якщо округлюється до найближчого мільйона, результат повинен бути 1000000, а не 2000000. У випадку 362.42499999999995 округлюючи до 2 DP, те, що повинно визначати напрямок округлення, - це третій десятковий
знак

76

Розглянемо .toFixed()та .toPrecision():

http://www.javascriptkit.com/javatutors/formatnumber.shtml


1
toFixed додає десяткових знаків до кожного значення незалежно від того, що.
stinkycheeseman

13
Обидва тут марні
Ісаїлія

На жаль, обидві функції додадуть додаткові знаки після коми, які @stinkycheeseman, схоже, не хочуть.
jackwanders


2
вони повертають рядки, а не числа, тому вони форматують і не обчислюють ..
saimiris_devel

63

Точний метод округлення. Джерело: Mozilla

(function(){

    /**
     * Decimal adjustment of a number.
     *
     * @param   {String}    type    The type of adjustment.
     * @param   {Number}    value   The number.
     * @param   {Integer}   exp     The exponent (the 10 logarithm of the adjustment base).
     * @returns {Number}            The adjusted value.
     */
    function decimalAdjust(type, value, exp) {
        // If the exp is undefined or zero...
        if (typeof exp === 'undefined' || +exp === 0) {
            return Math[type](value);
        }
        value = +value;
        exp = +exp;
        // If the value is not a number or the exp is not an integer...
        if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
            return NaN;
        }
        // Shift
        value = value.toString().split('e');
        value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
        // Shift back
        value = value.toString().split('e');
        return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
    }

    // Decimal round
    if (!Math.round10) {
        Math.round10 = function(value, exp) {
            return decimalAdjust('round', value, exp);
        };
    }
    // Decimal floor
    if (!Math.floor10) {
        Math.floor10 = function(value, exp) {
            return decimalAdjust('floor', value, exp);
        };
    }
    // Decimal ceil
    if (!Math.ceil10) {
        Math.ceil10 = function(value, exp) {
            return decimalAdjust('ceil', value, exp);
        };
    }
})();

Приклади:

// Round
Math.round10(55.55, -1); // 55.6
Math.round10(55.549, -1); // 55.5
Math.round10(55, 1); // 60
Math.round10(54.9, 1); // 50
Math.round10(-55.55, -1); // -55.5
Math.round10(-55.551, -1); // -55.6
Math.round10(-55, 1); // -50
Math.round10(-55.1, 1); // -60
Math.round10(1.005, -2); // 1.01 -- compare this with Math.round(1.005*100)/100 above
// Floor
Math.floor10(55.59, -1); // 55.5
Math.floor10(59, 1); // 50
Math.floor10(-55.51, -1); // -55.6
Math.floor10(-51, 1); // -60
// Ceil
Math.ceil10(55.51, -1); // 55.6
Math.ceil10(51, 1); // 60
Math.ceil10(-55.59, -1); // -55.5
Math.ceil10(-59, 1); // -50

Хтось поставив це також на GitHub та npm: github.com/jhohlfeld/round10
Jo Liss

Ні, Math.round10(3544.5249, -2)повертається 3544,52 замість 3544,53
Матія

2
@Matija Wolfram alpha говорить також 3544.52. Ви хочете мінімізувати помилку між поточним числом та округлим наближенням. Найближче наближення 3544.5249до 2 знаків після коми 3544.52(помилка = 0,0049). Якби вона була 3544.53, помилка була б 0,0051. Ви робите послідовне округлення, тобто Math.round10 (Math.round10 (3544.5249, -3), -2), що дає більшу помилку округлення, а значить, не бажано.
користувач

3
@ Матія, з математичної точки зору, 3544.5249 округлене 3544.52, а не 3544.53, тому цей код правильний. Якщо ви хочете, щоб він округлив його до 3544,53 у подібних випадках (навіть жорсткий невірний), зробіть щось подібне:number += 0.00011
Божидар Сіканджич

@Matija: Я думаю, що функція працює як слід. Можливо, ви хочете повторити округлення на кшталт:Math.round10( Math.round10(3544.5249, -3) , -2)
jumxozizi

60

Жодна з знайдених тут відповідей не є правильною . @stinkycheeseman попросив округлити , ви все округлили число.

Для округлення скористайтеся цим:

Math.ceil(num * 100)/100;

15
Приклади введення та виведення показують, що, хоч у запитанні було сказано "закруглити ...", воно насправді мало бути "круглим ..."
JayDM

2
@stinkycheeseman вказував на помилку в конкретному випадку, він не хотів завжди
округлювати так, як цеіл

9
Під час тестування Math.ceil(1.1 * 100)/100;виявлена дивна помилка - вона повертається 1.11, тому що 1,1 * 100 - 110.00000000000001це абсолютно нові сучасні браузери Firefox, Chrome, Safari та Opera ... IE, по-старому, все ще думає 1.1*100=1100.
skobaljic

1
@skobaljic спробуйте Math.ceil(num.toFixed(4) * 100) / 100
treeface

1
@treeface Math.ceil((1.1).toFixed(4) * 100) / 100також повернеться 1.11у Firefox, проблема / помилка сучасних браузерів - це множення, і люди повинні знати про це (наприклад, я працював над лотерейною грою того часу).
скобалич

47

Ось простий спосіб зробити це:

Math.round(value * 100) / 100

Можливо, ви хочете йти вперед і зробити окрему функцію, щоб зробити це за вас:

function roundToTwo(value) {
    return(Math.round(value * 100) / 100);
}

Тоді ви просто передасте значення.

Ви можете покращити його для округлення до будь-якої довільної кількості десяткових знаків, додавши другий параметр.

function myRound(value, places) {
    var multiplier = Math.pow(10, places);

    return (Math.round(value * multiplier) / multiplier);
}

2
Це рішення є неправильним, см stackoverflow.com/questions/38322372 / ... , якщо ви вводите 156893,145 і навколо нього з вищеописаної функції ви отримуєте 156893.14 замість 156893.15 !!!
saimiris_devel

2
@saimiris_devel, вам вдалося знайти примірник, коли числове представлення чисел у JavaScript не вдається. Ви вірні, що він закручується неправильно. Але - це тому, що ваш номер вибірки при множенні на 100 вже розбитий (15689314.499999998). Дійсно, для повнофункціональної відповіді потрібна спеціально розроблена бібліотека, яка б враховувала невідповідності в обробці JavaScript реальними цифрами. В іншому випадку, ви, ймовірно, могли б визнати недійсними будь-яку відповідь на це запитання.
JayDM

36
+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12

(10).toFixed(2); // = 10.00
(10.12345).toFixed(2); // = 10.12

1
Це не завжди дає ті самі результати, які ви отримали, якби ви представили рядок свого номера і округлили його. Наприклад, +(0.015).toFixed(2) == 0.01.
Марк Амері

35

Для мене Math.round () не давав правильної відповіді. Я виявив, що toFixed (2) працює краще. Нижче наведено приклади обох:

console.log(Math.round(43000 / 80000) * 100); // wrong answer

console.log(((43000 / 80000) * 100).toFixed(2)); // correct answer


Важливо зазначити, що toFixed не здійснює округлення, і що Math.round просто округляє до найближчого цілого числа. Тому для збереження десяткових знаків нам потрібно помножити початкове число на кількість потужностей десяти, нулі яких представляють потрібну кількість десяткових знаків, а потім розділити результат на те саме число. У вашому випадку: Math.round (43000/80000 * 100 * 100) / 100. Нарешті може бути застосовано toFixed (2) для того, щоб у результаті завжди було два десяткові дроби (з необхідними нульовими нулями) - ідеально для правильного вирівнювання серії чисел, представлених вертикально :)
Турбо

7
Також важливо зазначити, що .toFIxed () видає рядок, а не число.
carpiediem

2
Це все ще не вирішує питання округлення 1.005. (1.005).toFixed(2)все ще результати до 1.00.
DPac

34

Використовуйте цю функцію Number(x).toFixed(2);


8
Згорніть все це Numberзнову, якщо ви не хочете, щоб він повернувся як рядок:Number(Number(x).toFixed(2));

5
NumberВиклик не потрібно, x.toFixed(2)працює.
bgusach

3
Необхідний дзвінок @bgusach, оскільки вираз x.toFixed (2) повертає рядок, а не число. Щоб знову перетворити на число, нам потрібно перетворити цифру
Мохан Рам

2
При використанні цього методу (1).toFixed(2)повертається 1.00, але запитуючий 1в цьому випадку потрібен .
Євген Мала

1
Це не працює, 1.005.toFixed(2)врожай, "1"коли це має бути "1.01".
Адам Ягош

33

2017 р.
Просто використовуйте рідний код.toFixed()

number = 1.2345;
number.toFixed(2) // "1.23"

Якщо вам потрібно бути суворим і додавати цифри, якщо потрібно, це можна використовувати replace

number = 1; // "1"
number.toFixed(5).replace(/\.?0*$/g,'');

3
Метод toFixed повертає рядок. Якщо ви хочете числовий результат, вам потрібно надіслати результат toFixed в parseFloat.
Замбоніллі

@Zambonilli Або просто помножте на 1, якщо потрібно. але оскільки фіксовану кількість більшість випадків призначені для відображення, а не для обчислення рядка - це правильний формат
pery mimon

2
-1; не тільки було toFixedзапропоновано декількома відповідями років, перш ніж ваш, але він не відповідає умові "лише за потреби" у питанні; (1).toFixed(2)дає, "1.00"де бажаючий бажає "1".
Марк Амері

Добре, зрозумів.
Додаю

Якщо ви використовуєте lodash, це ще простіше: _.round (число, decimalPlace) Видалено мій останній коментар, адже у нього проблема. Лодаш _. навколо працює, хоча. 1.005 з десятковим знаком 2 перетворює на 1,01.
Девін Філдс

32

Спробуйте це легке рішення:

function round(x, digits){
  return parseFloat(x.toFixed(digits))
}

 round(1.222,  2) ;
 // 1.22
 round(1.222, 10) ;
 // 1.222

Хтось знає, чи є якась різниця між цим і return Number(x.toFixed(digits))?

1
@JoeRocc ... не має значення, наскільки я можу бачити, оскільки це .toFixed()дозволяє лише для чисел.
petermeissner

4
Ця відповідь має ту саму проблему, яку згадували кілька разів на цій сторінці. Спробуйте round(1.005, 2)побачити результат 1замість цього 1.01.
MilConDoin

видається більшою проблемою округлення альго? - існує більше, ніж можна було б уявити: en.wikipedia.org/wiki/Rounding ... round(0.995, 2) => 0.99; round(1.006, 2) => 1.01; round(1.005, 2) => 1
petermeissner

31

Є кілька способів зробити це. Для таких людей, як я, варіант Лодаша

function round(number, precision) {
    var pair = (number + 'e').split('e')
    var value = Math.round(pair[0] + 'e' + (+pair[1] + precision))
    pair = (value + 'e').split('e')
    return +(pair[0] + 'e' + (+pair[1] - precision))
}

Використання:

round(0.015, 2) // 0.02
round(1.005, 2) // 1.01

Якщо ваш проект використовує jQuery або lodash, ви також можете знайти належне round метод у бібліотеках.

Оновлення 1

Я видалив варіант n.toFixed(2), тому що він невірний. Дякую @ avalanche1


Другий варіант поверне рядок з точно двома десятковими знаками. Питання задає десяткові крапки лише за потреби. Перший варіант кращий в цьому випадку.
Маркос Ліма

@MarcosLima Number.toFixed()поверне рядок, але перед ним символ плюс, інтерпретатор JS перетворить рядок у число. Це синтаксичний цукор.
stanleyxu2005

На Firefox alert((+1234).toFixed(2))показує "1234.00".
Маркос Ліма

На Firefox, alert(+1234.toFixed(2))кидає SyntaxError: identifier starts immediately after numeric literal. Я дотримуюся 1-го варіанта.
Маркос Ліма

У деяких крайніх випадках це не працює: спробуйте ( jsfiddle ) з 362.42499999999995. Очікуваний результат (як в PHP echo round(362.42499999999995, 2)) 362.43. Фактичний результат:362.42
Д-р Джанлуїджі Зейн Занеттіні

26

Якщо ви використовуєте бібліотеку lodash, ви можете скористатися круглим методом lodash, як описано нижче.

_.round(number, precision)

Наприклад:

_.round(1.7777777, 2) = 1.78

@Peter Набір функцій, які надає Lodash, дійсно хороший порівняно зі стандартним Javascript. Однак я чув, що Лодаш має певні проблеми з продуктивністю порівняно зі стандартними JS. codeburst.io/…
Pradeep

1
Я погоджуюся з вашою думкою, що при використанні lodash є недоліки в продуктивності. Я думаю, що ці питання є спільними для багатьох абстракцій. Але просто подивіться, скільки відповідей на цю тему і як інтуїтивні рішення не вдається для кращих випадків. Ми бачили цю схему за допомогою jQuery, і коренева проблема була вирішена, коли браузери прийняли загальний стандарт, який вирішив більшість наших випадків використання. Потім вузькі місця продуктивності були переміщені до двигунів браузера. Я думаю, те саме повинно трапитися і з квартирами. :)
Петро

26

Оскільки у ES6 існує "правильний" спосіб (без перекреслення статики та створення обхідних шляхів), щоб зробити це за допомогою toPrecision

var x = 1.49999999999;
console.log(x.toPrecision(4));
console.log(x.toPrecision(3));
console.log(x.toPrecision(2));

var y = Math.PI;
console.log(y.toPrecision(6));
console.log(y.toPrecision(5));
console.log(y.toPrecision(4));

var z = 222.987654
console.log(z.toPrecision(6));
console.log(z.toPrecision(5));
console.log(z.toPrecision(4));

тоді ви можете просто, parseFloatі нулі будуть "відходити".

console.log(parseFloat((1.4999).toPrecision(3)));
console.log(parseFloat((1.005).toPrecision(3)));
console.log(parseFloat((1.0051).toPrecision(3)));

Однак це не вирішує «проблему округлення 1.005» - оскільки це властиво тому , як обробляються дробові фракції .

console.log(1.005 - 0.005);

Якщо ви відкриті для бібліотек, ви можете використовувати bignumber.js

console.log(1.005 - 0.005);
console.log(new BigNumber(1.005).minus(0.005));

console.log(new BigNumber(1.005).round(4));
console.log(new BigNumber(1.005).round(3));
console.log(new BigNumber(1.005).round(2));
console.log(new BigNumber(1.005).round(1));
<script src="https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/2.3.0/bignumber.min.js"></script>


3
(1.005).toPrecision(3)як і раніше повертається 1.00замість 1.01насправді.
Джакомо

toPrecisionповертає рядок, який змінює потрібний тип виводу.
admduren

@Giacomo Це не вада .toPrecisionметоду, це специфіка чисел з плаваючою комою (які числа в JS) - спробуйте 1.005 - 0.005, він повернеться 0.9999999999999999.
shau-kote

1
(1).toPrecision(3)повертає "1,00", але запитуючий хотів мати 1в цьому випадку.
Євген Мала

1
Як сказав @Giacomo, ця відповідь, мабуть, плутає "значні цифри" з "округленням до числа десяткових знаків". toPrecisionчи є формат, а не останній, і не є відповіддю на питання ОП, хоча, здається, спочатку актуальним, він стає багато неправильним. Дивіться en.wikipedia.org/wiki/Significant_figures . Наприклад, Number(123.4).toPrecision(2)повернення "1.2e+2"та Number(12.345).toPrecision(2)повернення "12". Я також погоджуюся з точкою @ adamduren, що вона повертає рядок, що не є бажаним (не величезна проблема, але не бажана).
NEEK

23

MarkG і Lavamantis запропонували набагато краще рішення, ніж прийняте. Прикро, що вони не отримують більше грошей!

Ось функція, яку я використовую для вирішення питань з десятковою комою з плаваючою комою, також на основі MDN . Це навіть більш загальне (але менш стисле), ніж рішення Лавамантіса:

function round(value, exp) {
  if (typeof exp === 'undefined' || +exp === 0)
    return Math.round(value);

  value = +value;
  exp  = +exp;

  if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))
    return NaN;

  // Shift
  value = value.toString().split('e');
  value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));

  // Shift back
  value = value.toString().split('e');
  return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));
}

Використовуйте його з:

round(10.8034, 2);      // Returns 10.8
round(1.275, 2);        // Returns 1.28
round(1.27499, 2);      // Returns 1.27
round(1.2345678e+2, 2); // Returns 123.46

Порівняно з рішенням Лавамантіса, ми можемо зробити ...

round(1234.5678, -2); // Returns 1200
round("123.45");      // Returns 123

2
Ваше рішення не охоплює деяких випадків на відміну від рішення MDN. Хоча це може бути і коротшим, це не точно ...
astorije

1
раунд (-1835.665,2) => -1835.66
Хорхе Сампайо

21

Це може допомогти вам:

var result = Math.round(input*100)/100;

для отримання додаткової інформації ви можете подивитися за цим посиланням

Math.round (num) vs num.toFixed (0) та невідповідності браузера


1
Чому у світі прийнята відповідь має набагато більше голосів, ніж цей, оскільки вони практично однакові, але цей був опублікований через 1 хвилину після прийнятої?
Цитую Дейва

18

Найпростішим підходом було б скористатись toFixed, а потім зняти прострочені нулі за допомогою функції Number:

const number = 15.5;
Number(number.toFixed(2)); // 15.5
const number = 1.7777777;
Number(number.toFixed(2)); // 1.78

це працює не у всіх випадках. робити обширні тести, перш ніж публікувати відповіді
бабурао

@baburao Будь ласка, опублікуйте випадок, коли вищезазначене рішення не працює
Марчін Ванаго

число const = 15; Номер (число.toFixed (2)); //15.00 замість 15
Кевін Джангіані

1
@KevinJhangiani const число = 15; Номер (число.toFixed (2)); // 15 - Я протестував його як на новітньому Chrome, так і на Firefox
Marcin Wanago

@KevinJhangiani як дістатися 15.00? Числа в JS не зберігають десяткових знаків і будь-яке відображення автоматично скорочує надлишки десяткових знаків (будь-які нулі в кінці).
ВЛАЗ

16
var roundUpto = function(number, upto){
    return Number(number.toFixed(upto));
}
roundUpto(0.1464676, 2);

toFixed(2) тут 2 - це число цифр, до яких ми хочемо округлити це число.


цей .toFixed () простіший у виконанні. просто перегляньте це один раз.
Ritesh Dhuri


14

Найпростіший спосіб:

+num.toFixed(2)

Він перетворює його в рядок, а потім назад в ціле число / float.


Дякую за цю найпростішу відповідь. Однак що таке "+" у + число? Для мене це не спрацювало, де десятковий вал прийшов у рядку. Я зробив: (num * 1) .toFixed (2).
Етан

@momo просто змінити аргумент toFixed()на 3. Так було б +num.toFixed(3). Це працює так, як і належить, 1.005 округляється до 1.00, що дорівнює 1
bigpotato

1
@Edmund Повертається 1,01, а не 1,00
ммм

13

Ось метод-прототип:

Number.prototype.round = function(places){
    places = Math.pow(10, places); 
    return Math.round(this * places)/places;
}

var yournum = 10.55555;
yournum = yournum.round(2);

13

Використовуйте щось на кшталт цього "parseFloat (parseFloat (значення) .toFixed (2))"

parseFloat(parseFloat("1.7777777").toFixed(2))-->1.78 
parseFloat(parseFloat("10").toFixed(2))-->10 
parseFloat(parseFloat("9.1").toFixed(2))-->9.1

1
не, якщо неточність є суттєвою для плаваючого представлення. ви просто видалите його, а потім знову введете ту саму помилку, перетворивши її знову на плавати!
Бен Макінтайр

12

Одним із способів досягти такого округлення, лише за необхідності, є використання Number.prototype.toLocaleString () :

myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})

Це забезпечить саме той результат, який ви очікуєте, але як рядки. Ви все ще можете конвертувати їх назад у числа, якщо це не той тип даних, який ви очікуєте.


Це найчистіше рішення, яке існує на сьогоднішній день і уникає всіх складних питань з плаваючою точкою, але на підтримку MDN все ще неповно - Safari поки не підтримує передачу аргументів toLocaleString.
Марк Амері

@MarkAmery В даний час тільки Android Browser є деякі проблеми: caniuse.com/#search=toLocaleString
ptyskju

12

Провівши різні ітерації всіх можливих способів досягти справжньої точності десяткового округлення, зрозуміло, що найбільш точним та ефективним рішенням є використання Number.EPSILON. Це забезпечує справжнє математичне рішення проблеми точності математики з плаваючою комою. Його можна легко заповнити, як показано тут: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON для підтримки всіх останніх користувачів IE (тоді, можливо, ми знову слід припинити це робити).

Адаптовано з рішення, яке надано тут: https://stackoverflow.com/a/48850944/6910392

Просте рішення, яке забезпечує точне десяткове округлення, підлогу та стелю, з необов'язковою змінною точності без додавання цілої бібліотеки.

ОНОВЛЕННЯ: Як зазначив Сергій у коментарях, існує обмеження на цей (або будь-який) метод, на який варто звернути увагу. У випадку таких цифр, як 0,014999999999999999, ви все одно будете мати неточності, які є результатом попадання на абсолютні межі точності обмежень для зберігання значень з плаваючою комою. Немає математичного чи іншого рішення, яке можна застосувати для обліку цього, оскільки саме значення негайно оцінюється як 0,015. Ви можете підтвердити це, просто викликавши це значення самостійно в консолі. Через це обмеження неможливо було б навіть використовувати маніпуляції з рядками для зменшення цього значення, оскільки його рядкове представлення просто "0,015". Будь-яке рішення для обліку цього необхідно застосувати логічно до джерела даних, перш ніж приймати значення в сценарій,

var DecimalPrecision = (function(){
        if (Number.EPSILON === undefined) {
            Number.EPSILON = Math.pow(2, -52);
        }
        this.round = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.round((n + r) * o) / o;
        }
        this.ceil = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.ceil((n + r) * o) / o;
        }
        this.floor = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.floor((n + r) * o) / o;
        }
        return this;
    })();
    console.log(DecimalPrecision.round(1.005));
    console.log(DecimalPrecision.ceil(1.005));
    console.log(DecimalPrecision.floor(1.005));
    console.log(DecimalPrecision.round(1.0049999));
    console.log(DecimalPrecision.ceil(1.0049999));
    console.log(DecimalPrecision.floor(1.0049999));
    console.log(DecimalPrecision.round(2.175495134384,7));
    console.log(DecimalPrecision.round(2.1753543549,8));
    console.log(DecimalPrecision.round(2.1755465135353,4));


1
(DecimalPrecision.round (0,014999999999999999, 2)) // повертається 0,02
Сергій

Гарний улов! Проблема полягає у зберіганні з плаваючою комою в JS, завжди будуть деякі крайні випадки. Хороша новина полягає в тому, що математика, яку ви застосуєте до Number.EPSILON, спочатку може бути більш тонко налаштована, щоб витіснити ці крайові справи далі на край. Якщо ви хочете гарантувати відсутність можливості для крайових випадків, вашим єдиним реальним рішенням буде маніпуляція з рядком, а потім математика. Щойно ви виконуєте будь-які математичні обчислення значення (навіть намагаючись перемістити десяткову), ви вже створили помилку.
KFish

Насправді, при подальшій інспекції це пов'язано не з будь-якою математикою, а проблема виявляється відразу після виклику вказаного значення. Ви можете підтвердити це, просто ввівши це число в консоль і побачивши, що воно негайно оцінюється до 0,015. Отже, це означало б абсолютний край точності для будь-якого числа з плаваючою точкою в JS. У цьому випадку ви навіть не можете перетворити на рядок і маніпулювати, оскільки значення рядка було б "0,015"
KFish,

11

Це найпростіше, елегантніше рішення (і я найкращий у світі;):

function roundToX(num, X) {    
    return +(Math.round(num + "e+"+X)  + "e-"+X);
}
//roundToX(66.66666666,2) => 66.67
//roundToX(10,2) => 10
//roundToX(10.904,2) => 10.9

4
Це хороший спосіб переписати прийняту відповідь, щоб прийняти аргумент, використовуючи Eнотацію.
AxelH

1
У деяких крайніх випадках це не працює: спробуйте ( jsfiddle ) roundToX(362.42499999999995, 2). Очікуваний результат (як в PHP echo round(362.42499999999995, 2)) 362.43. Фактичний результат:362.42
Д-р Джанлуїджі Зене Занеттіні

6
ІМХО, ваш результат PHP неправильний. Незалежно від того, що настане після третьої десяткової, якщо третя десяткова нижча за 5, то друга десяткова частина повинна залишатися такою ж. Це математичне визначення.
Soldeplata Saketos

1
Щоб бути ще більш лаконічним, натомість "e +" може бути просто "e".
Lonnie Best
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.