153

I found myself in the situation where I wanted to convert a BigInt value to a Number value. Knowing that my value is a safe integer, how can I convert it?

2
  • 4
    Are you referring to the Stage 3 proposal? That would be documented there. Commented Dec 29, 2018 at 15:03
  • Nice @Xufox, thanks for the edit. Commented Dec 29, 2018 at 15:14

4 Answers 4

229

Turns out it's as easy as passing it to the Number constructor:

const myBigInt = BigInt(10);  // `10n` also works
const myNumber = Number(myBigInt);

Of course, you should bear in mind that your BigInt value must be within [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER] for the conversion to work properly, as stated in the question.

Sign up to request clarification or add additional context in comments.

2 Comments

I wonder why +myBigInt is not supported yet.
31

You can use parseInt or Number

const large =  BigInt(309);
const b = parseInt(large);
console.log(b);
const n = Number(large);
console.log(n);

6 Comments

For performance reasons, I suggest not using parseInt for this. Yes, yes, according to the specs, both parseInt and Number ought to perform the conversion by converting the BigInt to a string before to a number. Nevertheless, the semantics of parseInt compared to the semantics of Number especially in view of how BigInt(number) yields a BigInt makes it more likely that browser vendors will apply more optimizations to Number(bigValue) than parseInt(bigValue) such that the stringification is internally skipped. Thus, I suggest using Number instead of parseInt for performance.
@JackGiffin please see this stackoverflow.com/questions/4090518/… where parseInt can be better in some cases
I am afraid that I do not understand. I agree that parseInt and Number have different behaviors, but, for the purposes of converting a BigInt to a number, I can think of no cases where they would differ. Perhaps you could provide an example of where Number and parseInt differ in conversion of BigInts to numbers to enlighten me. Thank you very much.
@JackGiffin You are totally right in this case of BigInts and what I need to show is that parseInt and Number have different behaviors and you can see the example in the link above.
@I_Al-thamary: best practice is to use radix while using parseInt
|
4

Avoiding Caveats

I know the scope of the original question is knowing you already have a "safe integer", but I wanted to give scope to others that land here how they should try to handle it.

Number.isSafeInteger

One would think you could utilize Number.isSafeInteger, but something trivial like the following snippet will show that utilizing the function on a BigInt will always be false even if the value is within the minimum and maximum bounds of a Number.

console.log(Number.isSafeInteger(BigInt(1)))
// Returns false

An Implementation using Results

This utilizes the TS-Results library to return a success or error response, rather than doing something like throwing.

If you do not want to use the Results library, you could instead return the value and throw otherwise, or handle it however you see appropriate.

import { Err, Ok, Result } from 'ts-results'

export function bigIntToNumber(value: bigint): Result<number, Error> {
  if (validInterger(value)) {
    return Ok(Number(value))
  }

  return Err(new Error(`Value: "${value}" is out of bounds to be a Number`))
}

function validInteger(value: bigint): boolean {
  return value <= Number.MAX_SAFE_INTEGER && value >= Number.MIN_SAFE_INTEGER
}

An Aside on Data Types and Data Ranges

Something interesting to note is that a JS number go up to 9,007,199,254,740,991 (the Number.MAX_SAFE_INTEGER) while your classic 4-byte integer would only be 2,147,483,647, thus a JS Number can hold more, but not as much as an 8-byte integer which goes up to 9,223,372,036,854,775,807.

As noted in comments there can be numbers past Number.MAX_SAFE_INTEGER up to Number.MAX_VALUE that are still technically valid JS Numbers. You can read more here:

1 Comment

Your last paragraph, as currently phrased, is incorrect. For example, 10_000_000_000_000_000_000 is totally a valid JS number. I know what you mean (2**53 - 1 is between 2**31 - 1 and 2**63 - 1, sure!), but that's not what that paragraph is currently saying. Another way of putting it: Number.MAX_SAFE_INTEGER != Number.MAX_VALUE. You're talking about the former, but JS numbers "go up to" (or "can hold" up to) the latter.
2

Edit: see the discussion in the comments below as to why this answer is not correct. I am leaving the answer up regardless for disambiguation.

You should use either of the static methods:

BigInt.asIntN() - Clamps a BigInt value to a signed integer value, and returns that value. BigInt.asUintN() - Clamps a BigInt value to an unsigned integer value, and returns that value.

as documented here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#static_methods

4 Comments

@zr0gravity7 @naveen, I had the chance to test this and these methods do not return numbers, but BigInt values instead. Their names are misleading, as the N suggests Number, when in fact they still return BigInt values. Try running typeof BigInt.asIntN(64, 1n) to see that it reports bigint. So this answer actually does not answer the question.
True that. A confusing name. Was reading the documentation. <3
The explanation of the name is that "intN" is the generalization of "int32", "int64", "int555" and so on; or put differently: "intN" is short for "N-bit integer". The value of this N is the first parameter to the function. An alternative spec could have provided BigInt.asInt64(x) and BigInt.asInt32(x), but for maximum flexibility the decision was made to have a single BigInt.asIntN(N, x) instead.
as(Ui|I)ntN does not do what you claim nor does this answer OP's question. These functions take a BigInt and return a BigInt modulo 2^N.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.