In ECMAScript, given a non-negative, finite double x
, is the following assertion always true?
Math.sqrt(x) === Math.pow(x, 0.5)
I know that both Math.sqrt()
and Math.pow()
are implementation-approximated, and the result may vary across platforms. However, are they truly interchangeable, or are there cases where this assertion evaluates to false?
I wrote a simple test program to check for counterexamples, but couldn't find any:
let x;
while (true) {
x = Math.random();
if (!Object.is(Math.sqrt(x), Math.pow(x, 0.5))) {
break;
}
}
I also found this question, where the assertion fails in Python. However, when testing the same example in JavaScript, I couldn't reproduce the error.
Can this assertion ever be false? Or is it guaranteed to be always true?
In ECMAScript, given a non-negative, finite double x
, is the following assertion always true?
Math.sqrt(x) === Math.pow(x, 0.5)
I know that both Math.sqrt()
and Math.pow()
are implementation-approximated, and the result may vary across platforms. However, are they truly interchangeable, or are there cases where this assertion evaluates to false?
I wrote a simple test program to check for counterexamples, but couldn't find any:
let x;
while (true) {
x = Math.random();
if (!Object.is(Math.sqrt(x), Math.pow(x, 0.5))) {
break;
}
}
I also found this question, where the assertion fails in Python. However, when testing the same example in JavaScript, I couldn't reproduce the error.
Can this assertion ever be false? Or is it guaranteed to be always true?
Share Improve this question asked Feb 8 at 7:20 dolmokdolmok 1588 bronze badges 6 | Show 1 more comment3 Answers
Reset to default 5I know that both
Math.sqrt()
andMath.pow()
are implementation-approximated, and the result may vary across platforms. However, are they truly interchangeable, or are there cases where this assertion evaluates to false?
Since you know the results of Math.sqrt(x)
and Math.pow(x, 0.5)
are not fully specified and the results of either one of them can vary between ECMAScript implementations, the only question remaining is whether the results of the two of them can differ in a single ECMAScript implementation. Scanning discussion of “implementation-approximated” in ECMAScript 2024 Language Specification does not show any constraints that require them to be equal.
It is not even clear that the language specification requires the approximations to produce the same value every time. That lack would allow Math.sqrt(1) === Math.sqrt(1)
to evaluate as false.
Additionally, if x
is a NaN or is negative, we expect the results of each operation to be a NaN, in which case the ===
operator evaluates as false.
Since I couldn’t reproduce this issue on a modern implementation, I tried the example from the Python question in Internet Explorer 5 in a Windows 98 VM:
alert(Math.sqrt(8885558) - Math.pow(8885558, 0.5));
and got pretty much the same answer:
Given that the ECMA-262 specification places so few constrains on the fidelity of floating-point arithmetic, this seems entirely compliant even with modern versions of it.
In general, I expect there will be a discrepancy between Math.sqrt(x)
and Math.pow(x, 0.5)
each time the former is implemented with an intrinsic, but the latter as equivalent to Math.exp(0.5 * Math.log(x))
– as that creates one more opportunity for rounding to introduce imprecision than a direct implementation. (Only one, because multiplication of a binary float by a power of two is always exact, as long as it is within representable range.)
glibc’s pow(a, b)
seems to be implemented by an algorithm that makes it more precise than a naïve exp(b * log(a))
, but I have not studied it in detail.
However, are they truly interchangeable, or are there cases where this assertion evaluates to false?
Algorithms exist for sqrt()
to return a result within 0.5 ULP of the exact answer.
If Math.pow(x, y)
tests for y == 0.5
and then performs sqrt()
, a likewise high-quality result is possible.
Yet general Math.pow(x, y)
implementations suffers from Table-maker's dilemma:
Why can't yw be rounded within half an ulp like SQRT? Because nobody knows how much computation it would cost...
Well crafted Math.pow(x, y)
can achieve near the 0.5 ULP ideal, yet may return a slightly inferior result from time-to-time. It is a trade-off between how much internal precision is used and execution time.
pow(x, 0.5)
to compute sqrt(x) instead of exp(½ln(x)), with one rounding step instead of three. – dumbass Commented Feb 8 at 7:34x
is non-negative and finite, I believeMath.sqrt(x)
should never returnNaN
. – dolmok Commented Feb 8 at 8:44