How to Convert a String to a Number in JavaScript? Integers, Floats, Big Numbers, and Edge Cases Explained.

How to Convert a String to a Number in JavaScript? Integers, Floats, Big Numbers, and Edge Cases Explained.
Photo by Carlos Irineu da Costa / Unsplash

This article is a deep dive into the wacky world of number conversion in JavaScript. (And it only scratches the surface.) The method you’ll use will greatly depend on the type of data you’re working with. For example:

  • Are these just integers or can they be decimal (float) numbers?
  • How big can these numbers be? How small can they be?
  • Are they base 10 (decimal) numbers, or are you dealing with a different base like hexadecimal?
  • What about Infinity? How do you want to handle NaN?
  • What about different formatting types based on language?

The Number Constructor

Just providing the string to the Number constructor might be enough for your use case. Examples:

Number("123") // => 123
Number("-123") // => -123
Number("123.45") // => 123.45 (floating point number)
Number("12e5") // => 1200000
Number("0.12e-5") // => 0.0000012
Number("Infinity") // => Infinity

But there’s also some interesting edge cases:

Number("12e") // => NaN
Number("123,000") // => NaN
Number("") // => 0 (this one is very dangerous!)

This method is attractive because of the versitility, but also very dangerous. Be sure to cover your code with unit tests so edge cases don’t slip through!

Using parseInt and Handling Different Bases (Radix)

parseInt is a built-in JavaScript function that parses a string argument and returns an integer of the specified base (radix). If you know you are only dealing with integers, this is your best best.

parseInt("123") // => 123
parseInt("-123") // => -123

The method has a second parameter, which is 10 by default, indicating that the conversion should be base 10. But you can change this:

parseInt("fff", 16) // => 4095
parseInt("0101", 2) // => 5

Things that are not integers, will end up being converted to NaN:

parseInt("Infinity") // => NaN
parseInt("") // => NaN

But you should be aware of these edge cases:

parseInt("123.45") // => 123
parseInt("123,000") // => 123
parseInt("12e5") // => 12

Making Floats with parseFloat

I hope that whatever you’re doing with floats is managable, because these can become quite a nightmare. Here’s some examples of what you can expect:

parseFloat("123") // => 123
parseFloat("-123") // => -123
parseFloat("123.300") // => 123.3
parseFloat("12e5") // => 1200000
parseFloat("12e-5") // => 0.00012
parseFloat("123,300") // => 123
parseFloat("123,300.00") // => 123
parseFloat("Infinity") // => Infinity
paeseFloat("") // => NaN

Two questions arise here:

  1. What is the level of precision?
  2. How do you handle different locales? Some might format the number 123456 as 123,456.00 and others 123.456,00, depending on the country.

Precision and toPrecision

As you may have figured out by now all JavaScript numbers are the same Number type. There is no special float data type. All numbers are internally stored in double-precision 64-bit binary format IEEE 754. (Just like double in Java or C#.) The Number type keeps 17 decimal places of precision. The largest value a Number can hold is about 2^53 -1. This value is represented by the constant Number.MAX_SAFE_INTEGER. (Another lie, since it’s not actually an integer.) You can check this yourself:

Math.pow(2, 53) - 1 === Number.MAX_SAFE_INTEGER // => true

If you need to format your number to a set precission, the toPrecision method is there:

const a = 123.456
console.log(a.toPrecision(10)) // => "123.4560000"

Different Locales

Unfortunetly, there is no built-in way to distinguish between different locales when it comes to parsing numbers. You will have to look in userland for libraries that accomplish this task.

BigInt

Need to represent really, really big numbers? You can use the BigInt object or just append n to an integer to cast it into this type. Examples:

const hugeNum1 = BigInt("904240240249024902094290407199254740991");
const hugeNum2 = 904240240249024902094290407199254740991n;
hugeNum1 === hugeNum2 // => true
typeof hugeNum1 === 'bigint' && typeof hugeNum2 === 'bigint' // => true

BigInt Comparison and Equality

But also note that there some interesting things when trying to compare BigInt with numbers. For example, strict equality comparison will fail, while loose comparison can succeed:

const num = 0;
const bigNum = 0n;
num1 === bigNum // => false, they are not of the same type!
num == bigNum // => true, once cast to the same type their values are equal

Other comparison operations work as expected:

0n < 1 // true
1n > 0 // true
2n > 2 // false
2n < 2 // false

BigInt and Sorting

Since the above operations work as expected, it is possible to sort an array of BitInts using the default behaviour of Array.sort. It’s also possible to sort a mixed array of numbers and BigInts!

const mixed = [4n, 6, -10n, 10, 4, 0, 0n]
mixed.sort() // default sorting behavior
// =>  [ -10n, 0, 0n, 10, 4n, 4, 6 ]

But as you might see, this is not the type of sorting you were perhaps expecting. This is because the default sort does not compare the numbers by value. Instead, the the default sort order is ascending and based upon converting the elements into strings, then comparing their sequences of UTF-16 code values.

So the usual way to sort numbers by value is to provide a custom sort function, like this:

const mixed = [10, 2, -1, 0, 6];
mixed.sort((a, b) => a - b);
// => [ -1, 0, 2, 6, 10 ]

But this won’t worked with our mixed array:

const mixed = [4n, 6, -10n, 10, 4, 0, 0n]
mixed.sort((a, b) => a - b);
// => Uncaught TypeError: can't convert BigInt to number

BigInt Operation

You can perform regular operations such as +, -, *, ** and % between two BigInts, but you cannot mix them with regular numbers. If you’re dealing with BigInts, then cast every operand into a BigInt to ensure there is no loss of precision or runtime error.

BigDecimal

Sadly, this type does not exist in JavaScript and you’ll have to look into userland solutions to find a library that fits your needs.

Summary

I’d suggest using the option that fits the use case the best and thoroughly testing your data transformation code with unit tests. For me this is usually parseInt as integers are usually what I’m working with in my web apps. Even if what you need is decimal, you can think about repesenting it as an integer.

For, example $9.99 can be stored internally as 999 cents and formatted as 9.99 on the UI.

Be careful to account for the possibility of NaN or Infinity and handling such errors gracefully!

Filip Danic

Filip Danic

Software Development Manager at Amazon and recovering JavaScript enthusiast. Sharing my experience and insights with the broader tech community via this blog!
The Netherlands