2.9999999999999999 >> .5?
-
05-07-2019 - |
Вопрос
Я слышал, что вы могли бы сдвинуть число вправо на .5 вместо использования Math.floor().Я решил проверить его пределы, чтобы убедиться, что это подходящая замена, поэтому я проверил следующие значения и получил следующие результаты в Google Chrome:
2.5 >> .5 == 2;
2.9999 >> .5 == 2;
2.999999999999999 >> .5 == 2; // 15 9s
2.9999999999999999 >> .5 == 3; // 16 9s
После некоторой возни я обнаружил, что максимально возможное значение из двух, которое при сдвиге вправо на .5 даст 2, равно 2.9999999999999997779553950749686919152736663818359374999999 (с повторением 9) в Chrome и Firefox.Это число равно 2.9999999999999997779 в IE.
Мой вопрос заключается в следующем:какое значение имеет число .0000000000000007779553950749686919152736663818359374?Это очень странное число, и оно действительно возбудило мое любопытство.
Я пытался найти ответ или хотя бы какой-то шаблон, но я думаю, что моя проблема заключается в том, что я действительно не понимаю побитовую операцию.Я понимаю идею в принципе, но сдвиг последовательности битов на .5 для меня не имеет вообще никакого смысла.Любая помощь приветствуется.
Для справки, странная последовательность цифр меняется на 2 ^x.Максимально возможные значения следующих чисел, которые по-прежнему усекаются должным образом:
for 0: 0.9999999999999999444888487687421729788184165954589843749¯ for 1: 1.9999999999999999888977697537484345957636833190917968749¯ for 2-3: x+.99999999999999977795539507496869191527366638183593749¯ for 4-7: x+.9999999999999995559107901499373838305473327636718749¯ for 8-15: x+.999999999999999111821580299874767661094665527343749¯ ...and so forth
Решение
На самом деле, вы просто в конечном итоге выполняете floor() для первого операнда, без каких-либо операций с плавающей запятой.Поскольку побитовые операции сдвига влево и сдвига вправо имеют смысл только с целочисленными операндами, движок JavaScript сначала преобразует два операнда в целые числа:
2.999999 >> 0.5
Становится:
Math.floor(2.999999) >> Math.floor(0.5)
Что, в свою очередь, является:
2 >> 0
Сдвиг на 0 бит означает "не выполнять сдвиг", и поэтому в итоге вы получаете первый операнд, просто усеченный до целого числа.
Исходный код SpiderMonkey содержит:
switch (op) {
case JSOP_LSH:
case JSOP_RSH:
if (!js_DoubleToECMAInt32(cx, d, &i)) // Same as Math.floor()
return JS_FALSE;
if (!js_DoubleToECMAInt32(cx, d2, &j)) // Same as Math.floor()
return JS_FALSE;
j &= 31;
d = (op == JSOP_LSH) ? i << j : i >> j;
break;
Вы видите "округление в большую сторону" с определенными числами из-за того, что движок JavaScript не может обрабатывать десятичные цифры с определенной точностью, и поэтому ваше число в конечном итоге округляется в большую сторону до следующего целого числа.Попробуйте сделать это в вашем браузере:
alert(2.999999999999999);
Вы получите 2,999999999999999.Теперь попробуйте добавить еще одну цифру 9:
alert(2.9999999999999999);
Ты получишь оценку "3".
Другие советы
Возможно, это самая худшая идея, которую я когда-либо видел.Его единственная возможная цель для существования - выиграть конкурс запутанного кода.Опубликованные вами длинные числа не имеют никакого значения - это артефакт базовой реализации с плавающей запятой, отфильтрованный через бог знает сколько промежуточных уровней.Сдвиг битов на дробное число байтов является безумием, и я удивлен, что это не вызывает исключения - но это Javascript, всегда готовый переопределить "безумие".
На вашем месте я бы никогда не пользовался этой "функцией".Его единственное значение - как возможная первопричина необычного состояния ошибки.Использование Math.floor()
и сжальтесь над следующим программистом, который будет поддерживать этот код.
Подтверждая пару подозрений, которые возникли у меня при чтении вопроса:
- Сдвиг вправо любого дробного числа
x
любым дробным числомy
будет просто усекатьсяx
, дающий тот же результат , что иMath.floor()
при этом основательно сбивая читателя с толку. - 2.999999999999999777955395074968691915...это просто наибольшее число, которое можно отличить от "3".Попробуйте оценить его сам по себе - если вы что-нибудь к нему добавите, он будет оценен в 3.Это артефакт реализации браузера и локальной системы с плавающей запятой.
Если вы хотите углубиться, прочитайте "Что должен знать каждый ученый об арифметике с плавающей запятой": http://docs.sun.com/source/806-3568/ncg_goldberg.html
Я не думаю, что ваш правильный сдвиг имеет значение. Вы просто превышаете разрешение константы с плавающей запятой двойной точности.
В Chrome:
var x = 2.999999999999999777955395074968691915273666381835937499999;
var y = 2.9999999999999997779553950749686919152736663818359375;
document.write("x=" + x);
document.write(" y=" + y);
Распечатывает: x = 2.9999999999999996 y = 3
Попробуйте этот JavaScript: оповещение (parseFloat (& Quot; 2,9999999999999997779553950749686919152736663818359374999999 & Quot;)); р>
Тогда попробуйте это: оповещение (parseFloat (& Quot; 2,9999999999999997779553950749686919152736663818359375 & Quot;)); р>
То, что вы видите, - это простая неточность с плавающей точкой. Дополнительную информацию об этом см., Например: http://en.wikipedia.org/wiki / Floating_point # Accuracy_problems .
Основная проблема заключается в том, что самое близкое значение, которое может получить значение с плавающей запятой к представлению второго числа, больше или равно 3, тогда как значение закрытия, которое поплавок может получить к первому числу, строго меньше трех. р>
Что касается того, почему смещение вправо на 0,5 делает что-либо вменяемым, кажется, что 0.5 само по себе заранее преобразуется в int (0). Затем исходный тип float (2.999 ...) преобразуется в int путем усечения, как обычно.
Оператор сдвига вправо работает только с целыми числами (обе стороны). Таким образом, смещение вправо на 0,5 бита должно быть точно эквивалентно смещению вправо на 0 бит. И перед операцией сдвига левая часть преобразуется в целое число, что делает то же самое, что и Math.floor ().
Я подозреваю, что преобразование 2.9999999999999997779553950749686919152736663818359374999999 в его двоичное представление было бы поучительным.Вероятно, это всего лишь на 1 бит отличается от true 3.
Хорошая догадка, но сигары нет.Поскольку число FP двойной точности имеет 53 бита, последнее число FP перед 3 на самом деле является (точным):2.999999999999999555910790149937383830547332763671875
Но почему это так 2.9999999999999997779553950749686919152736663818359375
(и это точно, а не 49999...!)
который является более высокий чем последняя отображаемая единица измерения ?Округление.Процедура преобразования (строка в число) просто правильно запрограммирована на округление входных данных до следующего числа с плавающей запятой.
2.999999999999999555910790149937383830547332763671875
.......(значения между, увеличивающиеся) -> округлить в меньшую сторону
2.9999999999999997779553950749686919152736663818359375
.......(значения между, возрастающие) -> округлить до 3
3
Входные данные преобразования должны использовать полную точность.Если число составляет ровно половину между этими двумя числами fp (что равно 2,9999999999999997779553950749686919152736663818359375) округление зависит от установленных флагов.Округление по умолчанию - округление до четного, что означает, что число будет округлено до следующего четного числа.
Сейчас
3 = 11.(двоичный)
2.999...= 10.11111111111......(двоичный)
Все биты заданы, число всегда нечетное.Это означает, что точная половина числа будет округлена в большую сторону, так что вы получаете странный период .....49999, потому что он должен быть меньше точной половины, чтобы его можно было отличить от 3.
Я подозреваю, что преобразование 2.9999999999999997779553950749686919152736663818359374999999 в его двоичное представление было бы поучительным. Это, вероятно, только 1 бит отличается от истинного 3.
И чтобы добавить к ответу Джона, шансы того, что он будет более производительным, чем Math.floor, ничтожно малы.
Я не знаю, использует ли JavaScript числа с плавающей точкой или какую-то библиотеку с бесконечной точностью, но в любом случае вы получите ошибки округления при такой операции, даже если она довольно хорошо определена. р>
Следует отметить, что число " .0000000000000007779553950749686919152736663818359374 " вполне возможно, Epsilon , определяемый как " наименьшее число E, такое что (1 + E ) > 1. & Quot; р>