Вопрос

Я слышал, что вы могли бы сдвинуть число вправо на .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;

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top