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
:
- Исходный текст
.99999999999999999
преобразуется в число. Ближайшее число равно 1, так что это результат. (Следующий ближайший номер 0,999999999999999988897769753748434595763683319091796875, то есть 1-2–53.) - Затем 1 сравнивается с 1. Результат верен.
В (1-Number.MIN_VALUE) === 1
:
Number.MIN_VALUE
2–1074, около 5e–304.- 1–2 –1074 чрезвычайно близко к единице. Точное значение не может быть представлено в виде числа, поэтому используется ближайшее значение. Опять же, ближайшее значение равно 1.
- Затем 1 сравнивается с 1. Результат верен.
В Math.ceil(Number.MIN_VALUE)
:
Number.MIN_VALUE
2–1074, около 5e–304.- Функция потолка этого значения равна 1.
В Math.floor(.999999999999)
:
- Исходный текст
.999999999999
преобразуется в число. Ближайший номер 0,9999999999900002212172012150404043495655059814453125, так что это результат. - Функция пола этого значения равна 0.
В Math.floor(.99999999999999999)
:
- Исходный текст
.99999999999999999
преобразуется в число. Ближайшее число равно 1, так что это результат. - Функция пола 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 нет такого числового значения.