Skip to content
This repository has been archived by the owner on Oct 10, 2019. It is now read-only.

Document the BigInt(large number) footgun #170

Closed
joshuajbouw opened this issue Oct 6, 2018 · 10 comments
Closed

Document the BigInt(large number) footgun #170

joshuajbouw opened this issue Oct 6, 2018 · 10 comments

Comments

@joshuajbouw
Copy link

joshuajbouw commented Oct 6, 2018

For some reason, the numbers are getting a bit mixed up slightly. Will contain just the relevant code.

const iv = new BigUint64Array([
    BigInt(0x6A09E667F3BCC908), BigInt(0xBB67AE8584CAA73B),
    BigInt(0x3C6EF372FE94F82B), BigInt(0xA54FF53A5F1D36F1),
    BigInt(0x510E527FADE682D1), BigInt(0x9B05688C2B3E6C1F),
    BigInt(0x1F83D9ABFB41BD6B), BigInt(0x5BE0CD19137E2179)
  ])

const ctx = {
  h: new BigUint64Array(8)
}

for (let i = 0; i < 8; i++) {
    ctx.h[i] = iv[i]
}

What I expected: For 0x6A09E667F3BCC908 to be in ctx.h[0]

What happened: 0x6a09e667f3bcc800 ended up in ctx.h[0]

I am running latest Node.

This is kind of a big problem =). I didn't know where else to put this.

@ljharb
Copy link
Member

ljharb commented Oct 6, 2018

the 0x-prefixed numbers will be normal numbers before a BigInt is created, which can cause precision loss if the numbers are large enough.

I’d suggest using the literal syntax, or passing strings to the BigInt constructor.

@joshuajbouw
Copy link
Author

Passed it as a string, works.

Passed: 7640891576956012808

As a string: 7640891576956012808
As a number returns: 7640891576956012544
As a hex returns: 7640891576956012544

The precision loss kind of sucks for my application, but, I guess I have to make due for now. I am a little bit worried about the outcome at the end as quite a bit will happen to this variable.

@jakobkummerow
Copy link
Collaborator

jakobkummerow commented Oct 6, 2018

Just use literals: instead of BigInt(0x123) (or the more cumbersome BigInt("0x123")), write 0x123n.

You don't need arrays to see the difference in precision between Numbers and BigInts, the interactive Node shell or Chrome's DevTools console is enough:

// Number -> BigInt -> String has Number precision:
> BigInt(0x6A09E667F3BCC908).toString(16)
< "6a09e667f3bcc800"
// String -> BigInt -> String has BigInt precision:
> BigInt("0x6A09E667F3BCC908").toString(16)
< "6a09e667f3bcc908"
// BigInt -> String has BigInt precision (of course):
> (0x6A09E667F3BCC908n).toString(16)
< "6a09e667f3bcc908"
// Number -> String has Number precision (of course):
> (0x6A09E667F3BCC908).toString(16)
< "6a09e667f3bcc800"

As a number returns: 7640891576956012544
As a hex returns: 7640891576956012544

"number" and "hex" are really "decimal Number" and "hex Number" -- both are Numbers, neither has more or less precision than the other. In other words, 0x6A09E667F3BCC908 and 7640891576956012808 are entirely equivalent notations for the exact same Number value (which is 7640891576956012544).

Note that I'm capitalizing the term "Number". In JavaScript, "Number" refers to a specific data type, which is distinct from BigInts. Writing 1 gives you a Number, writing 1n gives you a BigInt. Numbers have limited precision (64-bit "double" floating-point representation, see IEEE 754 for the full spec), BigInts have arbitrary precision (well, up to some implementation-defined limit; it should be safe to expect at least a million bits). The limited precision of Numbers is the reason why BigInts were introduced. Your code is running into these limits, so you should use BigInts instead of Numbers. There is no "worrisome precision loss", only the well-known limits of the Number precision, which BigInts let you avoid.

@joshuajbouw
Copy link
Author

Great fantastic, thanks @jakobkummerow. The only problem is that Typescript doesn't seem to understand what 'n' means yet, so going to have to resort to BigInt everything for now. This should be fun >.>

@littledan
Copy link
Member

littledan commented Oct 7, 2018

Seems like we should call this out explicitly in the documentation, as a gotcha to avoid, cc @sarahgp. Cc @DanielRosenwasser for TypeScript.

@littledan littledan changed the title Numbers change when copying to another array Document the BigInt(large number) footgun Jan 5, 2019
@kyranet
Copy link
Contributor

kyranet commented Feb 10, 2019

I'm a little late, but TypeScript@3.2.0 released BigInt, @joshuajbouw.

Is this issue still unresolved? I could try to open a PR if nobody has claimed this yet.

@littledan
Copy link
Member

Yes, documentation is still needed here, and your help would be very much appreciated!

@DanielRosenwasser
Copy link
Member

Sorry, I'm lost in the context here. Is this just a foot-gun that trying to write a really big numeric literal and passing it as an argument to BigInt(...) isn't the same as passing the more-appropriate string literal? And we want to write something about this somewhere?

@littledan
Copy link
Member

Yes, exactly. Probably in this repo and MDN. Nothing needed on the TS side, though I am happy to review any documentation or blog posts you might be writing if you'd like.

@DanielRosenwasser
Copy link
Member

Check out microsoft/TypeScript#29863

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants