JavaScript Math.floor: как будет округляться номер гарантии?

Я хочу нормализовать массив так, чтобы каждое значение находилось в [0-1) .. т.е. максимум никогда не будет равен единице, а минимум может быть равен 0. Это мало чем отличается от случайной функции, возвращающей числа в одном и том же диапазоне.

Глядя на это, я обнаружил, что.99999999999999999 === 1 верно! То же самое (1-Number.MIN_VALUE) === 1 Но Math.ceil(Number.MIN_VALUE) равен 1, как и должно быть. Некоторые другие: Math.floor(.999999999999) равен 0, а Math.floor(.99999999999999999) равен 1

Итак, в JS есть проблемы с округлением.

Можно ли как-нибудь нормализовать набор чисел, лежащий в диапазоне [0,1)?

3 ответа

Решение

Это может помочь изучить шаги, которые выполняет JavaScript для каждого из ваших выражений.

В .99999999999999999===1:

  1. Исходный текст .99999999999999999 преобразуется в число. Ближайшее число равно 1, так что это результат. (Следующий ближайший номер 0,999999999999999988897769753748434595763683319091796875, то есть 1-2–53.)
  2. Затем 1 сравнивается с 1. Результат верен.

В (1-Number.MIN_VALUE) === 1:

  1. Number.MIN_VALUE 2–1074, около 5e–304.
  2. 1–2 –1074 чрезвычайно близко к единице. Точное значение не может быть представлено в виде числа, поэтому используется ближайшее значение. Опять же, ближайшее значение равно 1.
  3. Затем 1 сравнивается с 1. Результат верен.

В Math.ceil(Number.MIN_VALUE):

  1. Number.MIN_VALUE 2–1074, около 5e–304.
  2. Функция потолка этого значения равна 1.

В Math.floor(.999999999999):

  1. Исходный текст .999999999999 преобразуется в число. Ближайший номер 0,9999999999900002212172012150404043495655059814453125, так что это результат.
  2. Функция пола этого значения равна 0.

В Math.floor(.99999999999999999):

  1. Исходный текст .99999999999999999 преобразуется в число. Ближайшее число равно 1, так что это результат.
  2. Функция пола 1 равна 1.

Здесь есть максимум две удивительные вещи. Во-первых, цифры в исходном тексте преобразуются во внутренние числовые значения. Но это не должно удивлять. Конечно, текст должен быть преобразован во внутренние представления чисел, и тип Number не может идеально хранить все бесконечно много чисел. Так что это должно округлить. И, конечно, цифры очень близки от 1 раунда до 1.

Другая, возможно, удивительная вещь в том, что 1-Number.MIN_VALUE равен 1. Но на самом деле это та же проблема: точный результат не представим, но он очень близок к 1, поэтому используется 1.

Math.floor Функция работает правильно. Он никогда не вносит каких-либо ошибок, и вам не нужно ничего делать, чтобы гарантировать его округление. Это всегда так.

Однако, поскольку вы хотите нормализовать числа, кажется, что в какой-то момент вы собираетесь делить числа. При делении могут возникнуть проблемы с округлением, поскольку многие результаты деления не совсем представимы, поэтому они должны быть округлены.

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

Javascript будет обрабатывать любое число от 0,9999999999999999994 до 1 как 1, поэтому просто вычтите.000000000000000006.

Конечно, это не так просто, как кажется, так как.000000000000000006 оценивается как 0 в Javascript, так что вы можете сделать что-то вроде:

function trueFloor(x)
{
    x = x * 100;
    if(x > .0000000000000006)
        x = x - .0000000000000006;
    x = Math.floor(x/100);
    return x;
}

РЕДАКТИРОВАТЬ: Или, по крайней мере, вы думаете, что могли бы. Очевидно, JS преобразует.99999999999999999 в 1, прежде чем передать его в функцию, поэтому вам придется попробовать что-то вроде:

trueFloor("0.99999999999999999")

function trueFloor(str)
{
    x=str.substring(0,9) + 0;
    return Math.floor(x); //=> 0
}

Не уверен, зачем вам нужен такой уровень точности, но в теории, я думаю, это работает. Вы можете увидеть рабочую скрипку здесь

Пока вы произносите свою безумно точную float как stringэто, вероятно, ваш лучший выбор.

Пожалуйста, поймите одну вещь: это...

.999999999999999999

... это просто числовой литерал. Как только

.999999999999999998
.999999999999999997
.999999999999999996
...

... вы видите шаблон.

Как JavaScript обрабатывает эти литералы - это совсем другая история. И да, эта обработка ограничена количеством битов, которые могут использоваться для хранения числового значения.

Количество возможных литералов с плавающей точкой бесконечно по определению - независимо от того, насколько мал диапазон для них установлен. Например, возьмите те, которые показаны выше: сколько numbers very close to 1 Вы можете выразить? Да, это бесконечно: просто продолжайте добавлять 9 на линию.

Но контейнер для каждого Number значение вполне конечно: оно имеет 64 бита. Это означает, что он может хранить 2^64 различных значений (Infinite, -Infinite а также NaN среди них) - и все.

Вы хотите работать с такими литералами в любом случае? Используйте Strings для их хранения, а не Numbers - и некоторую библиотеку BigMath JS (на ваш выбор), чтобы снова работать с этими значениями - как Strings.

Но из вашего вопроса похоже, что вы не так, как вы говорили о массиве Numbers - Числовые значения, то есть. И ни в коем случае не может быть .999999999999999999 хранится там, так как в JavaScript нет такого числового значения.

Другие вопросы по тегам