Today’s major Web programming languages, such as PHP or Python, include exponents (typically the symbol ^ or **). Exponentiation has also been added in the latest ES7, using the symbol **, and the latest Chrome already provides support for exponentiation.

But in javascript, ** operations are sometimes not equal to math.pow (a,b), and in the latest Chrome 55:

Math.pow(99,99) = 3.697296376497263e+197,

But the result of 99**99 is 3.697296376497268e+197.

They are not equal

3.697296376497263 3.697296376497268 e+197 e+197

And math.pow (99,99) -99 **99 is not 0, but -5.311379928167671e+182.

So we suspect that the ** operator is just another implementation of exponentiation. But when we write a function, exponents behave strangely:

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

Calling diff(99) returns 0. WTF? They’re equal again!

Guess what the following code outputs?

var x = 99;
x**x - 99**99;
Copy the code

The result of running this code is -5.311379928167671e+182.

It’s a power of Schrodinger.

The reason is that the V8 engine uses const folding. Constant folding is a compiler optimization technique.

Consider the following code:

for (let i = 0; i < 100*100*100; I++){// loop body}Copy the code

The condition I <100*100*100 of this loop is an expression. If it is evaluated in judgment, the calculation of 100*100*100 will be carried out 1,000,000 times. If the compiler were to merge constants during parsing, the loop would look like this:

for (let i = 0; i < 1000000; I++){// loop body}Copy the code

The calculation of 99**99 mentioned above also uses constant folding. That is, 99**99 is evaluated at compile time (constant folding), whereas math.pow is always evaluated at run time. When exponentials are used (example a**b) there is no constant folding, so the value of a**b is evaluated at run time, and ** is compiled into math.pow calls.

In the source SRC /parsing/parser.cc file, compile time computes the code:

case Token::EXP: { double value = Pow(x_val, y_val); int int_value = static_cast<int>(value); *x = factory()->NewNumberLiteral( int_value == value && value ! = 0.0? int_value : value, pos, has_dot); return true;Copy the code

You can see that the Pow function was used to evaluate the exponentiation. Pow is an inline function that does some general optimization internally, or STD :: Pow (x, y) is used to calculate the final result if it cannot be optimized.

Math.pow’s algorithm is:

// ES6 section 20.2.2.26 Math. Pow (x, y) TF_BUILTIN(CodeStubAssembler) {Node* x = Parameter(1); Node* y = Parameter(2); Node* context = Parameter(5); Node* x_value = TruncateTaggedToFloat64(context, x); Node* y_value = TruncateTaggedToFloat64(context, y); Node* value = Float64Pow(x_value, y_value); Node* result = ChangeFloat64ToTagged(value); Return(result); }Copy the code

So they’re using different algorithms. But when constant folding is not done, ** is converted to math.pow function calls:

Expression* Parser::RewriteExponentiation(
    Expression* left, 
    Expression* right,
    int pos) {
  ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
  args->Add(left, zone());
  args->Add(right, zone());
  return factory()->NewCallRuntime(Context::MATH_POW_INDEX, args, pos);
}
Copy the code

This leads to the weird problem that ** sometimes does not equal Math.pow. Take a look at the following code:

console.log(99**99);
a = 99, b = 99;
console.log(a**b);
console.log(Math.pow(99, 99));
Copy the code

Output respectively:

3.697296376497268 e+197 e+197 e+197 3.697296376497263 3.697296376497263

Actually,

9999 = 3697296376497267726571879056288054405956687642817411024302599724235525704552775234214106500101282327279409788895483 26540119429996769494359451621570193644014418071060667659301384999779999159200499899

So the first result is closer to the exact value.

This weird behavior was submitted to the V8 project as a bug #5848 last week (January 16, 2017).

Why is math.pow () (sometimes) not equal to ** in JavaScript?