Питання Круглий строк не повинно перевищувати 2 десяткових знаків (лише за необхідності)


Я хотів би обійти не більше ніж 2 десяткових знаків, але тільки при необхідності.

Вхід:

10
1.7777777
9.1

Вихід:

10
1.78
9.1

Як це зробити? JavaScript?


1845
2017-08-06 17:17


походження


Я зробив скрипку з багатьма техніками, запропонованими як рішення тут ... так що ви можете порівняти: скрипка - dsdsdsdsd
Ніхто, мабуть, не знає про це Number.EPSILON. Використовуйте Math.round( num * 100 + Number.EPSILON ) / 100. - cronvel
Для нових читачів ви не можете це зробити якщо у вас немає результату типу рядка. Математика з плаваючою точкою на подвійних внутрішніх представленнях чисел означає, що є завжди номери, які не можуть бути представлені як акуратні десяткові знаки. - Walf


Відповіді:


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


2292
2017-08-06 17:20



Хоча це буде працювати в більшості випадків, він не буде працювати на 1.005, що, нарешті, вийде на 1, а не на 1.01 - James
@ Джеймс Вау, це дуже дивно - я працюю на консолі Chrome dev, і я помічаю, що 1,005 * 100 = 100,49999999999999. Math.round (100.49999999999999) оцінює до 100, тоді як Math.round (100.5) оцінює до 101. IE9 робить те ж саме. Це пов'язано з плаваюча куля дивність в javascript - stinkycheeseman
Простий обхід. Для 2 d.p., використовуйте Math.round((num + 0.00001) * 100) / 100. Спробуй Math.round((1.005 + 0.00001) * 100) / 100 і Math.round((1.0049 + 0.00001) * 100) / 100 - mrkschan
@mrkschan Чому це працює, і що це нерозумно для всіх номерів? - CMCDragonkai
Для тих з вас, хто цього не розуміє, ця техніка називається масштабуванням. У принципі, що тут дається відповідь, виведіть дві цифри через десяткову точку, перетворивши цифру на ціле число, щоб уникнути усіх сумнівних питань з плаваючою комою, навколо цього, а потім перекласти його назад у те, що було раніше, поділивши на 100, і у вас є відповідь на 2dp - Alex_Nabu


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

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

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


2327
2017-10-11 00:27



Цей один (toFixed) підхід хороший, і він працював для мене, але це конкретно робить ні дотримуватися оригінального запиту "лише тоді, коли це необхідно". (Він має рази від 1,5 до 1,50, що порушує специфікації.) - Per Lundberg
Для вимоги "при необхідності" виконайте наступні дії: parseFloat(number.toFixed(decimalPlaces));   @PerLundberg - Onur Yıldırım
parseFloat("55.555").toFixed(2) повертає "55.55" на консолі Chrome dev. - Levi Botelho
Немає переваг використання toFixed замість Math.round; toFixed призводить до досить однакових проблем округлення (спробуйте їх з 5.555 і 1.005), але це як 500x (не жартує) повільніше, ніж Math.round ... Схоже, відповідь @MarkG - точніше тут. - Pierre
Чому ж такі дивні? - D.Deriso


Ви можете використовувати

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

324
2017-08-21 12:56



@Redsandro +(val) є примусовим еквівалентом використання Number(val). Зізвання "e-2" до числа призвело до того, що рядок потрібно було перетворити на число. - Jack
Остерігайтеся, що для великих і крихітних поплавців, які будуть виробляти NaN, оскільки, наприклад, + "1e-21 + 2" не будуть правильно розібрані. - Pierre
Ви "вирішили" проблему 1.005, але представили нову: зараз, на консолі Chrome roundToTwo(1.0049999999999999) виходить як 1.01 (неминуче, оскільки 1.0049999999999999 == 1.005) Мені здається, що плаваю ви, якщо вводите num = 1.005 "очевидно" "повинен" кругтися до 1.00, оскільки точне значення num становить менше 1.005. Звичайно, мені також здається, що рядок '1.005', очевидно, '' should '' повинен бути округлений до 1.01. Той факт, що різні люди, здається, мають різні інтуїції про те, що тут актуальна правильна поведінка, є частиною того, чому це складно. - Mark Amery
Між ними немає числа (з плаваючою комою) 1.0049999999999999 і 1.005, тому за визначенням вони однакові. Це називається дедукідним вирізом. - Azmisov
@ Азімов правий. Поки 1.00499 < 1.005 є true, 1.0049999999999999 < 1.005 оцінює до false. - falconepl


Відповідь 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');
  });
})

115
2017-11-01 07:40



П'єр підняв серйозну проблему з відповіддю Марк. - dsjoerg
Примітка. Якщо ви не бажаєте змінювати номер. Proototype - просто напишіть це як функцію: function round(number, decimals) { return +(Math.round(number + "e+" + decimals) + "e-" + decimals); } - Philipp Tsipman
Я створив Math.roundPlaces(num, places), так що він також буде працювати на рядках (на всякий випадок). - bradlis7
Спробуй n: 1e + 19 - це повертає NaN - DavidJ
Цей алгоритм завжди округляє (замість далеко від нуля). Отже, у випадку негативних чисел виходом не те, що ви могли очікувати: (-1.005).round(2) === -1 - Aleksej Komarov


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

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

71
2017-10-21 17:02



Чому це не найкраща відповідь? (EDIT: о, тому що він перетворюється в String :) - Daniel
Також тому, що він додає кінцеві нулі, тобто не те, що запитали оригінальне питання. - Alastair Maw
Але кінцеві нулі легко зняти з регулярним виразом, тобто "Номер (10.10000.toFixed (2). Replace (/ 0 + $ /, ''))) = = 10.1 - Chad McElligott
@ daniel the верхня відповідь такий же (як зараз), але це не так все одно круглий правильно, спробуй +(1.005).toFixed(2) який повертається 1 замість 1.01. - Emile Bergeron
@ ChadMcElligott: ваш регулярний вираз не працює добре з цілими числами: Number(9).toFixed(2).replace(/0+$/, '') => "9." - Jacob van Lingen


Точний метод округлення. Джерело: 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

54
2017-08-01 08:02



Хтось поставив це на GitHub і npm, а також: github.com/jhohlfeld/round10 - Jo Liss
Прохолодний і робочий код. спасибі @ Error. !! - Anahit DEV
Нах Math.round10(3544.5249, -2) повертає 3544,52 замість 3544,53 - Matija
@ Матія Вольфрам альфа каже 3544,52 теж. Ви хочете мінімізувати помилку між поточним числом та округленою наближеності. Найближче наближення 3544.5249до 2 десяткових знаків є 3544.52 (помилка = 0.0049). Якщо це було 3544.53, помилка буде 0.0051. Ви виконуєте послідовне округлення, тобто Math.round10 (Math.round10 (3544.5249, -3), -2), що дає більшу помилку округлення і, отже, не бажано. - user
@Matija, з математичної точки зору, 3544.5249 округлений становить 3544.52, а не 3544.53, тому цей код правильний. Якщо ви хочете, щоб він округлили його до 3544,53 в цьому і випадках, як це (навіть важко невірно), робіть щось на зразок цього: number += 0.00011 - Bozidar Sikanjic


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

Щоб округлити, використовуйте це:

Math.ceil(num * 100)/100;

53
2018-06-13 09:35



Приклад вводу та виводу показує, що, хоча питання "круглий ..." було насправді призначено "круглий ...". - JayDM
@stinkycheeseman вказує на помилку в конкретному випадку, він не хотів завжди рахувати, як цель робить, він просто хотів 0,005 до раунд до 0,01 - mjaggard
Виявлено помилку під час тестування Math.ceil(1.1 * 100)/100; - повертаєш 1.11, тому що 1.1 * 100 є 110.00000000000001 за новими сучасними браузерами Firefox, Chrome, Safari і Opera ... IE, як і раніше, вважає 1.1*100=1100. - skobaljic
@skobaljic спробуйте Math.ceil(num.toFixed(4) * 100) / 100 - treeface
@ Treeface Math.ceil((1.1).toFixed(4) * 100) / 100 також повернеться 1.11 в Firefox сучасна проблема / помилка браузера з розмноженням, і люди повинні знати про це (наприклад, я працював на лотерею). - skobaljic