Description
I’m spinning this out of #13.
We should keep
sqrt
andcbrt
for BigInts because we havepow
. The truncation towards zero behavior is unsurprising (it matches/
) and mathematically useful, both directly and in various algorithms.For example, if you want to compute an arbitrary-precision square root of a BigInt, the truncated square root provides a great first step of the algorithm. You then square the truncated square root, subtract it from the original number, and proceed with the algoritchm.
For another example, if you want to compute a truncated square root of a BigInt to 2 decimal places, multiply your original BigInt by 10000n, take the truncated square root, and you'll get the answer times 100n.
Another example: Suppose you want to compute the square root of a BigInt n rounded to the nearest integer instead of truncated. This is how you'd do it:
roundedSqrt = (Math.sqrt(4n * n) + 1n)/2n
Combining the two examples above, here's how to compute the square root of a BigInt n rounded to nearest to two decimal places:
let t = (Math.sqrt(40000n * n) + 1n)/2n; let i = t / 100n; // Integral part of result let f = t % 100n; // 00-99 decimal part of result
sqrt
andcbrt
are just inverses of the most common cases ofpow
. It would be as weird to have one and not the other as it would be to have*
but not/
. They're easy and very lightweight to implement — the implementation cost of including them is trivial enough that it's not worth the effort to conduct developer surveys.I'm afraid we're getting into analysis paralysis and design-by-voting rather than picking the simplest option, which is including the Math functions that mathematically make sense.
Suppose you want to compute the square root of a BigInt n rounded to the nearest integer
Then I suggest you do
Math.round(Math.sqrt(Number(my_bigint)))
. In fact, if you're interested in results rounded to integer (or to two decimal places, for that matter), then your entire calculation is probably better off with Numbers.sqrt and cbrt are just inverses of the most common cases of pow. It would be as weird to have one and not the other
That argument cuts both ways:
pow
is a legacy function rendered obsolete by the introduction of**
, so there's little reason to extend it in any way (for BigInts or otherwise). So if the only reason to havesqrt
is that we havepow
, but the latter isn't really motivated other than by "because we could", then we might as well have neither of them.the implementation cost of including them is trivial enough
From that claim, in turn, one could also conclude that it's perfectly fine to leave implementations to user space, especially as long as we know of no use cases.
sqrt and cbrt are just inverses of the most common cases of pow. It would be as weird to have one and not the other
That argument cuts both ways:
pow
is a legacy function rendered obsolete by the introduction of**
, so there's little reason to extend it in any way (for BigInts or otherwise). So if the only reason to havesqrt
is that we havepow
, but the latter isn't really motivated other than by "because we could", then we might as well have neither of them.For what it’s worth, I would like to gently push back against the notion that
pow
is merely a “legacy function”.pow
remains in use today in functional programming as a reducible and partially applicable function object. I would be quite surprised if it weren’t still being used in new JavaScript code today. It’s not like it’s the actually deprecatedwith
statement.And if
pow
is still being used in new code today, then it will remain surprising whenever it doesn’t act like**
.In addition, as long as we’re assuming that “BigInt
sqrt
is useful as long as BigIntpow
is in the language”, I would argue that BigInt**
also does count as being “in the language”. Under the previous assumption, BigIntsqrt
is useful as long as BigInt**
is in the language.sqrt
being an inverse of**
should be just as important assqrt
being an inverse ofpow
.
To be accurate, let's keep the distinction between "
sqrt
is useful" (of which no evidence has been presented) and "it's weird not to havesqrt
" (which is an opinion that some individuals have expressed).
Then I suggest you do Math.round(Math.sqrt(Number(my_bigint))).
And then you'd sometimes get the wrong answer.
In fact, if you're interested in results rounded to integer (or to two decimal places, for that matter), then your entire calculation is probably better off with Numbers.
Yes, lots of things can be done using Numbers. That doesn't say anything about the cases where you want guaranteed precision and rounding/truncating behavior. I'm not interested in rehashing the debate about the general usefulness of BigInts.
the implementation cost of including them is trivial enough
From that claim, in turn, one could also conclude that it's perfectly fine to leave implementations to user space, especially as long as we know of no use cases.
One could reach an incorrect conclusion. I was referring to the amount of code this would take, which is minuscule — smaller than some of the comments on this thread. However, the knowledge required to do it correctly is quite specialized and not accessible to most users. Also, a user-space implementation would not work as well as a built-in one because it would not be able to take advantage of the internal representation.