tc39 / proposal-bigint-math

Draft specification for supporting BigInts in JavaScript’s Math methods.

Home Page:https://tc39.es/proposal-bigint-math/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

sqrt/cbrt/log2/log10 and rounding

Yaffle opened this issue · comments

in my Chrome:
Math.cbrt(131329**3-1)
gives
131329
if Math.cbrt(131329n**3n-1n) gives 131328 then you cannot expect the equal result for all safe integers.

the same is for Math.sqrt(67108865**2 - 1) === 67108865
and Math.log2(2**49 - 1) === 49, Math.log10(10**15 - 2) === 15

The range for which this holds depends on the accuracy (precision?) of the Math.sqrt and Math.cbrt, for Math.sqrt, I think, it is 0.5ulps, while for Math.cbrt it is implementation defined and limited to some small amount of ulps (4) .

function value
Math.cbrt 131329**3 - 1 ~= 2**51.00847800299688
Math.sqrt 67108865**2 - 1 ~= 2**52.00000004299566
Math.log2 2**49 - 1
Math.log10 10**15 - 2 ~= 2**49.82892142331043

Test:

for (var i = 0; 2**i < 2**53; i++) console.log(i, Math.log2(2**i-1) < i, Math.log2(2**i)>=i)
for (var i = 0; 10**i < 2**53; i++) console.log(i, Math.log10(10**i-1) < i, Math.log10(10**i)>=i)
var i = 1;
while (i**3 < 2**53 && Math.cbrt(i**3 - 1) < i && Math.cbrt(i**3) >= i) {
  i++;
}
console.log(i);
var i = 1;
while (i**2 < 2**53 && Math.sqrt(i**2 - 1) < i && Math.sqrt(i**2) >= i) {
  i++;
}
console.log(i);

Thank you for taking a look at the proposal! As an implementor of a BigInt library, you bring much-needed real-world insight.

I don’t think safe Number integers should be a problem, because in this proposal, sqrt, cbrt, log2, and log10 would all return BigInts when they are given BigInts. And those returned BigInts would be truncated towards 0, just like BigInt division /. So:

  • (3000000000n - 1n) / 1000000000n already returns 2n.
  • cbrt(131329n ** 3n - 1n) would return 131328n.
  • sqrt(67108865n ** 2n - 1n) would return 67108864n.
  • log2(2n ** 49n - 1n) would return 48n.
  • log10(10n ** 15n - 1n) would return 14n.

Does that make sense?

If so, I can edit the explainer to make this clearer.

@js-choi ,
It makes sense, I am just pointing to the fact, that Math.cbrt(131329**3-1) !== Number(Math.cbrt(131329n**3n-1n)) and so you cannot switch between safe integers and bigints. May be this is OK.

while Math.trunc(a / b) === Number(BigInt(a) / BigInt(b)) is always true for safe integers

This is true. This difference is a result of how Ecma 262 defines Number::divide(x, y) to return precisely 𝔽(ℝ(x) / ℝ(y)), while Math.sqrt and so on are currently defined to return implementation-approximated values.

I think this behavior should be okay, but I would also love to hear opinions from anyone else.

so for current plan and for number x:
Math.floor(Math.sqrt(x)) matches Number(Math.floor(Math.sqrt(BigInt(x)))),
Math.floor(Math.cbrt(x)) matches Number(Math.floor(Math.cbrt(BigInt(x)))),
Math.floor(Math.log2(x)) matches Number(Math.floor(Math.log2(BigInt(x)))),
Math.floor(Math.log10(x)) matches Number(Math.floor(Math.log10(BigInt(x)))),
when x is a positive integer and x is less than 2**49 - 1 and only if special values (a**2 - for sqrt, a**3 - for cbrt, 2**n - for log2 and 10**n - for log10) are speced to produce the exact result or a little greater then result. In most of the web browsers this is true.

@Yaffle: Thank you for the investigation! How did you test these—did you loop through all safe-integer Numbers, and which non-safe-integer Numbers did you also check? And which browsers did you test on?

@js-choi , I have updated my first comment to this issue with the javascript test

@js-choi I have tested on latest Safari, Firefox, Chrome, IE 11 on different operations systems (by using browsers of the visitors of my tool :-) )

Closing. sqrt, cbrt, log2, and log10 truncation may be developer surprising. Truncating log2 could be codified as a future type-overloaded Math.bitLength method. And there are no other known use cases for truncating sqrt, cbrt, log2, or log10.

See also #13 (comment), #14, and #14 (comment).

Thank you for making me aware of this! I will have to investigate this myself to see how this works. Do you know of any other use cases for truncated BigInt roots?

This would have ramifications for the rest of the proposal, so I might punt BigInt sqrt to a future proposal while we gather more data. Hopefully the Committee will have more stomach for BigInt sqrt if it accepts this initial minimal proposal.

@js-choi , I cannot single out other examples for sqrt, but I think, sqrt is definitly useful, although, it could be that more general floor(nthRoot(a, n)) is needed.

Btw, this library needs
gcd, bitLength, sqrt, modPow(this one for smal integers), and modInverse

Truncation is the only option with reasonable mathematical properties for BigInt sqrt, and it matches how BigInt division works. It always gives an exact integral answer that's mathematically the most useful and all implementations can agree on. I'm not sure what the controversy here is. Other alternatives such as trying to match the IEEE double rounding errors in Math.cbrt(131329**3-1) don't make sense — the whole point of using BigInts is avoiding such unpredictable or implementation-defined behavior.

Yeah, gcd and such would be useful.

@waldemarhorwat: As of right now, BigInt sqrt etc. have been removed due to lack of definite use cases, but some developers seem to think they might be useful, so I’m trying to figure out how with the TC39 Research Incubator Group.

The proposal’s current vision is to punt wholly new methods like Math.gcd or modPow to future proposals. This proposal is currently focusing on creating a precedent for such future proposals, with a minimal set of definitely useful type overloads of certain existing Math methods. So the vision is: We start with overloading Math.min, max, sign, and abs, and once that advances we make a Math.gcd/modPow/etc. proposals, each of which would handle both Numbers and BigInts.

Feedback on this vision would be welcome in #13 and #14!