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?