Почему Math.pow () () (иногда) не равно ** в JavaScript?

118

Я только что обнаружил функцию ECMAScript 7 a**bв качестве альтернативы Math.pow(a,b)( Ссылка MDN ) и наткнулся на обсуждение в этом посте , в котором они, по-видимому, ведут себя по-другому. Я протестировал его в Chrome 55 и могу подтвердить, что результаты отличаются.

Math.pow(99,99) возвращается 3.697296376497263e+197

в то время как

99**99 возвращается 3.697296376497268e+197

Таким образом, регистрация разницы Math.pow(99,99) - 99**99приводит к -5.311379928167671e+182.

Пока можно сказать, что это просто еще одна реализация, но упаковка ее в функцию снова ведет себя иначе:

function diff(x) {
  return Math.pow(x,x) - x**x;
}

вызов diff(99)возвращается 0.

Почему так происходит?

Как указал xszaboj , это можно сузить до этой проблемы:

var x = 99;
x**x - 99**99; // Returns -5.311379928167671e+182
Томас Альтманн
источник
7
Похоже, кто-то переписал используемый алгоритм, и была обнаружена ошибка с плавающей запятой . Цифры трудны ...
krillgar
4
@krillgar звучит разумно, но почему тогда такая же ошибка не возникает в функции?
Thomas Altmann
3
@AndersonPimentel Ссылка MDN указывает на таблицу совместимости .
Альваро Гонсалес
7
разница между этими двумя: var x = 99; х * * х; и 99 * * 99. Или функция diff (x) {return 99 * * 99 - (x * * x); }; Diff (99). Извините за интервал, комментарий фильтрует две звезды :(
xszaboj
1
@xszaboj помещает код в обратные кавычки, `likethis`чтобы сделать его читабельным, а также избежать проблем с жирным / курсивом
phuclv

Ответы:

126

99**99будет оценено во время компиляции ( «постоянного складывание»), а также компилятор powподпрограмма отличается от выполнения одного . При оценке **во время выполнения результаты идентичны Math.pow- неудивительно, поскольку **фактически компилируется для Math.powвызова:

console.log(99**99);           // 3.697296376497268e+197
a = 99, b = 99;
console.log(a**b);             // 3.697296376497263e+197
console.log(Math.pow(99, 99)); // 3.697296376497263e+197

Фактически

99 99 = 369729637649726772657187905628805440595668764281741102430259972423552570455277523421410650010128232727940978889548326540119429996769494359451621570193644014418093079984

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

Такое поведение похоже на ошибку в V8. Об этом было сообщено, и мы надеемся, что скоро все исправят.

Georg
источник
19
Значит, это в основном JS, пытающийся 99**99заранее повысить производительность вычислений ? Можно ли считать это ошибкой, поскольку Math.powсоздает одинаковые выходные данные для чисел и переменных, а **не?
Thomas Altmann
3
@ThomasAltmann: Math.rowвсегда выполняется, сворачивание констант может выполняться только для операторов. Да, это определенно ошибка.
georg
11
Ошибка была зарегистрирована , судя по всему OP здесь.
Джеймс Торп
5
Я использую MS Грань, и все 3 результаты одинаковы: 3.697296376497263e+197, 3.697296376497263e+197, и 3.697296376497263e+197соответственно. Это определенно ошибка Chrome.
Nolonar
4
@ThomasAltmann, если сворачивание констант дает худшее значение, чем имплицит времени выполнения, то это ошибка. Если он дает лучшее значение, чем время выполнения, то это может или не может считаться ошибкой. В этом случае лучше - правильное значение - «... 26772 ...», сворачивание констант дает «... 268» (округлено правильно), а среда выполнения дает «... 263» (отключается на 4+ ед. на последнем месте).
hobbs