Я нашел ошибку в операторе экспоненциирования V8

1656578288 ya nashel oshibku v operatore eksponencziirovaniya v8

Содержание статьи

Кристоф Мишель

A4T1txSLDHURXjOhslt0TlBAbAFB0Jz03yEA
Фото Ричи Валенса на Unsplash

Я всегда думал, что новый оператор подвода к степени ES6 x ** y было то же, что Math.pow(x,y).

Действительно, об этом говорится в спецификации Math.pow:

Возвратить результат применения оператора ** с основой и показателем, как указано в п. 12.6.4.

12.6.4. Применение оператора** утверждает, что результат есть зависит от реализации — но между ними не должно быть разногласий ** и Math.pow.

Однако оценка следующего в текущем механизме JS V8 (Chrome/Node) приводит к этому:

console.log('1.35 ** 92', 1.35 ** 92)                   // 978828715394.7672console.log('Math.pow(1.35, 92)', Math.pow(1.35, 92))   // 978828715394.767

Оператор подведения к степени ** возвращает более точное приближение.

Но это не единственная странность с оператором подведения к степени: давайте попробуем оценить то же самое с переменными (REPL) — это не должно иметь никакой разницы:

THt5NNcrKd1S9dd99OY4jUhPB0nw9wrHiY0K
const exponent = 92;console.log(`1.35 ** exponent`, 1.35 ** exponent)                   // 978828715394.767console.log('1.35 ** 92', 1.35 ** 92)                               // 978828715394.7672console.log(`Math.pow(1.35, exponent)`, Math.pow(1.35, exponent))   // 978828715394.767console.log('Math.pow(1.35, 92)', Math.pow(1.35, 92))               // 978828715394.767

Но это делает: 1.35 ** 92 отличается от 1.35 ** exponent.

Кажется, здесь происходит то, что компилятор обрабатывает код JS 1.35 ** 92 которая уже постоянно составлена

Это имеет смысл, поскольку V8 действительно компилируется в машинный код.

V8 повышает производительность, компилируя JavaScript в родной машинный код перед его исполнением по сравнению с байт-кодом или его интерпретацией.

V8 работает, сначала интерпретируя код JS с помощью их Интерпретатор зажигания. Он выполняет второй запуск с Компилятор TurboFan оптимизация машинный код

TyMWLEdnZyqL2oDHhD8mWqiYH0vsL6vgjp9J
По пониманию байт-кода V8

TurboFan теперь делает постоянное сложение. Его алгоритм подведения к степени имеет лучшую точность, чем алгоритм подведения к степени компилятора JIT (Ignition).

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

Это ошибка?

Я бы так сказал, хотя в моем коде это не было строго. Но это все еще не соответствует спецификации, говорящей Math.pow и ** должно привести к тому же значению.

Если вы транспилируете код с помощью babel, x ** y переводится на Math.pow(x,y), что опять-таки приводит к разногласиям между транспилированным и нетранспилированным кодом. Как мы видели, Math.pow(1.35, 92) есть нет оптимизируется (только операторов похоже, оптимизировано V8). поэтому 1.35 ** 92 приводит к другому коду при транспиляции в ES5.

Используя эту ошибку и пренебрегая любыми методами чистого кода, мы можем написать хорошую функцию, чтобы определить, работаем ли мы в Chrome (если вы не транспилируете свой код?):

function isChrome() {    return 1.35 ** 92 !== Math.pow(1.35, 92)}

Все еще более читабельны, чем строки агента пользователя. ?

Первоначально опубликовано на cmichel.io

9APsDGxF26LJJOgAusJLRrMPLw3zBSbLoP8u

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *