Skip to content

BigInt sqrt and cbrt #16

Open
0 of 1 issue completed
Open
0 of 1 issue completed
@js-choi

Description

@js-choi

I’m spinning this out of #13.

@waldemarhorwat:

We should keep sqrt and cbrt for BigInts because we have pow. 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 and cbrt are just inverses of the most common cases of pow. 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.

@jakobkummerow:

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 have sqrt is that we have pow, 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.

@js-choi:

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 have sqrt is that we have pow, 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 deprecated with 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 BigInt pow is in the language”, I would argue that BigInt ** also does count as being “in the language”. Under the previous assumption, BigInt sqrt is useful as long as BigInt ** is in the language. sqrt being an inverse of ** should be just as important as sqrt being an inverse of pow.

@jakobkummerow:

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 have sqrt" (which is an opinion that some individuals have expressed).

@waldemarhorwat:

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.

Sub-issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions