tc39 / proposal-bigint-math

Draft specification for supporting BigInts in JavaScript’s Math methods.
https://tc39.es/proposal-bigint-math/
BSD 3-Clause "New" or "Revised" License
36 stars 2 forks source link

sqrt/cbrt/log2/log10 and rounding #11

Closed Yaffle closed 3 years ago

Yaffle commented 3 years ago

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);
js-choi commented 3 years ago

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:

Does that make sense?

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

Yaffle commented 3 years ago

@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

js-choi commented 3 years ago

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.

Yaffle commented 3 years ago

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.

js-choi commented 3 years ago

@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?

Yaffle commented 3 years ago

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

Yaffle commented 3 years ago

@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 :-) )

js-choi commented 3 years ago

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 https://github.com/tc39/proposal-bigint-math/issues/13#issuecomment-919472001, https://github.com/tc39/proposal-bigint-math/issues/14, and https://github.com/tc39/proposal-bigint-math/issues/14#issuecomment-918663577.

Yaffle commented 3 years ago

@js-choi floor(sqrt(s)) is used in Continued Fraction Factorization method - https://github.com/Yaffle/continuedFractionFactorization/blob/main/continuedFractionFactorization.js#L36

js-choi commented 3 years ago

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.

Yaffle commented 3 years ago

@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

waldemarhorwat commented 3 years ago

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.

js-choi commented 3 years ago

@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!