Вернуться к уроку

Как получить дробную часть числа?

важность: 4

Напишите функцию getDecimal(num), которая возвращает десятичную часть числа:

alert( getDecimal(12.345) ); // 0.345
alert( getDecimal(1.2) ); // 0.2
alert( getDecimal(-1.2) ); // 0.2

P.S. Постарайтесь не использовать toFixed

Открыть песочницу с тестами для задачи.

Функция

Первая идея может быть такой:

function getDecimal(num) {
  return num - Math.floor(num);
}

alert( getDecimal(12.5) ); // 0.5
alert( getDecimal(-1.2) ); // 0.8, неверно!

Как видно из примера выше, для отрицательных чисел она не работает.

Это потому, что округление Math.floor происходит всегда к ближайшему меньшему целому, то есть Math.floor(-1.2) = -2, а нам бы хотелось убрать целую часть, т.е. получить -1.

Можно попытаться решить проблему так:

function getDecimal(num) {
  return num > 0 ? num - Math.floor(num) : Math.ceil(num) - num;
}

alert( getDecimal(12.5) ); // 0.5
alert( getDecimal(-1.2) ); // 0.19999999999999996, неверно!
alert( getDecimal(1.2) ); // 0.19999999999999996

Проблема с отрицательными числами решена, но результат, увы, не совсем тот.

Внутреннее неточное представление чисел приводит к ошибке в вычислениях, которая проявляется при работе и с положительными и с отрицательными числами.

Давайте попробуем ещё вариант – получим остаток при делении на 1. При таком делении от любого числа в остатке окажется именно дробная часть:

function getDecimal(num) {
  return num > 0 ? (num % 1) : (-num % 1);
}

alert( getDecimal(12.5) ); // 0.5
alert( getDecimal(1.2) ); // 0.19999999999999996, неверно!

В общем-то, работает, функция стала короче, но, увы, ошибка сохранилась.

Что делать?

Увы, операции с десятичными дробями подразумевают некоторую потерю точности.

Зависит от ситуации.

  • Если внешний вид числа неважен и ошибка в вычислениях допустима – она ведь очень мала, то можно оставить как есть.
  • Перейти на промежуточные целочисленные вычисления там, где это возможно.
  • Если мы знаем, что десятичная часть жёстко ограничена, к примеру, может содержать не более 2 знаков то можно округлить число, то есть вернуть +num.toFixed(2).

Если эти варианты не подходят, то можно работать с числом как со строкой:

function getDecimal(num) {
  var str = "" + num;
  var zeroPos = str.indexOf(".");
  if (zeroPos == -1) return 0;
  str = str.slice(zeroPos);
  return +str;
}

alert( getDecimal(12.5) ); // 0.5
alert( getDecimal(1.2) ); // 0.2

Открыть решение с тестами в песочнице.