最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Is Math.sqrt(x) and Math.pow(x, 0.5) equivalent? - Stack Overflow

programmeradmin1浏览0评论

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
  • 4 One edge case that comes to my mind is when x is NaN, as NaN is never equal to itself – Proteus Commented Feb 8 at 7:30
  • 3 You should check the source code of both functions. If a different implementation, then the outputs may not always be exactly the same, especially since floating point arithmetic itself is not exact. – Tim Biegeleisen Commented Feb 8 at 7:31
  • 2 This no longer reproduces for me in current Python either – I surmise libc implementations have in the meantime added a special case for 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:34
  • Oh never mind, I did not notice it was Windows-specific to begin with. – dumbass Commented Feb 8 at 7:59
  • 1 @Proteus If x is non-negative and finite, I believe Math.sqrt(x) should never return NaN. – dolmok Commented Feb 8 at 8:44
 |  Show 1 more comment

3 Answers 3

Reset to default 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?

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.

发布评论

评论列表(0)

  1. 暂无评论