Demigodd
@Demigodd

Как правильно сравнить десятичные числа в JS?

Есть ли способ сравнить decimal числа с учетом того что последнее чило у них может быть + или - 1 ?

Например
2.000000001 == 2 // true
4.9999999998 >= 4.9999999999 // true

7.99 == 8.02 //false
// Так как 7.99 равен только 7.98, 7.99, 8.00
// А 8.02 == 8.01, 8.02, 8.03


Возможно ли такое сравнение чисел в JS и как это грамотно реализовать ?
  • Вопрос задан
  • 1580 просмотров
Решения вопроса 4
SagePtr
@SagePtr
Еда - это святое
Math.abs(x - y) <= 0.01 к примеру
Ответ написан
@dimoff66
Кратко о себе: Я есть
Определяем количество знаков после запятой и разницу между числами умножаем
и округляем до целого (все эти операции, чтобы избавиться от js-ных неточностей)
Если целое не больше единицы, значит равно.

Если количество знаков после запятой определяется по первому переданному числу, то код следующий

const compare= (v, v2) => {
   const parts = String(v).split('.')
   const pow = parts.length > 1 ? parts.pop().length : 0     
   return Math.round(Math.abs(v - v2) * Math.pow(10, pow)) <= 1
}

console.log(compare(7.98, 7.99)) // true
console.log(compare(7.98, 7.97)) // true
console.log(compare(7.98, 8)) // false
console.log(compare(0.1, 0.15)) // true


если по числу с наибольшим количеством знаков после запятой, то такой

const compare= (v, v2) => {
   const parts = [v, v2].map(v => String(v).split('.'))
   const pow = Math.max(...parts.map(v => v.length > 1 ? v.pop().length : 0))
   return Math.round(Math.abs(v - v2) * Math.pow(10, pow)) <= 1
}

console.log(compare(7.98, 7.99)) // true
console.log(compare(7.98, 7.97)) // true
console.log(compare(7.98, 8)) // false
console.log(compare(0.1, 0.15)) // false
Ответ написан
sergiks
@sergiks Куратор тега JavaScript
♬♬
Можно прогнать числа через .toExponential() – это их нормализует:
(123.4567).toExponential() // "1.234567e+2"
и разбить на три части: до запятой [1, 10), после запятой и степень.
Три целых: [1, 234567, 2]

Для проверки «равенства ±1» обязательно, чтобы степень (третье число) совпала.
Разность вторых чисел - буквально вычитаем и берем Math.abs() - если равна нулю, сравниваем только первые числа - на разность не выше 1. Если первые числа равны, то разность вторых не превышать 1 должна.

С таким же преобразованием числа легко реализовать и другие операции сравнения: >=, <=

const fuzzy = {
  split: n => {
    const parts = n.toExponential().split('e')
      .map((p, i) => i ? parseInt(p) : p.split('.').map(x => parseInt(x)));
    if (parts[0].length < 2) parts[0].push(0);
    return [parts[0][0], parts[0][1], parts[1]];
  },
  
  eq: (a, b) => {
    a = fuzzy.split(a);
    b = fuzzy.split(b);
    
    if (a[2] !== b[2]) return false;
    const diff = Math.abs(a[1] - b[1]);
    if (diff === 0) return Math.abs(a[0] - b[0]) <= 1;
    if (a[0][0] !== b[0][0]) return false;
    return (diff <= 1);
  }
}


Использовать: fuzzy.eq( 12.1234, 12.1233) // true

тесты
const tests = [
  [1, 1, true],
  [111, 112, true],
  [0.001, .002, true],
  [0.001, .0025, false],
  [0.001, .003, false],
  [12.345, 1.2345, false],
  [123.88, 123.870, true],
  [-1.11111111, 1.11111110, false],
  [-1.11111111, -1.11111112, true],
  [2.000000001, 2, true],
  [4.9999999998, 4.9999999999, true],
  [7.9, 8.02, false],
  [7.99, 7.98, true],
  [7.99, 7.99, true],
  [7.99, 8.00, true],  // НЕ ПРОХОДИТ! Надо этот случай предусмотреть, но я ушёл спать.
];


let allok = true;
tests.forEach(test => {
  if (fuzzy.eq(...test) !== test[2]) {
    console.log("FAIL:", test);
    allok = false;
  } else {
    console.log("OK:", test);
  }
});
console.log(allok ? 'All OK!' : "Some tests FAILED");
Ответ написан
xmoonlight
@xmoonlight
https://sitecoder.blogspot.com
L=2.000000001;
R=2;

p=20; //precision
d=0.02; //delta

alert(Math.abs(L.toFixed(p)-R.toFixed(p))<=d?'true':'false');
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы