JavaScript Number Data Type

Number Basics

JavaScript has a single number type that covers both integers and decimals - there's no separate int or float. All numbers are stored as 64-bit double-precision floating-point values following the IEEE 754 standard. This is convenient because you don't have to think about types, but it comes with one well-known consequence: decimal arithmetic isn't always exact. Numbers can be written in decimal, hexadecimal (0x prefix), octal (0o prefix), binary (0b prefix), or scientific e-notation.

javascript
1// All of these are the same type: number
2let count = 42;
3let price = 19.99;
4const temperature = -5.5;
5
6// Alternative representations
7let scientific = 2.5e4;  // 25000
8let hex = 0xFF;          // 255
9let octal = 0o10;        // 8
10let binary = 0b1010;     // 10
11
12console.log(scientific); // 25000
13console.log(hex);        // 255
14console.log(typeof 42);  // "number"
15console.log(typeof 3.14); // "number" - same type

Number Characteristics

  • Single number type - One type covers both integers and decimals. No int, float, or double distinction.
  • 64-bit floating point - All numbers use IEEE 754 double-precision format, which determines both the range and the precision limits.
  • Multiple formats - Decimal, hex (0xFF), octal (0o10), binary (0b1010), and scientific notation (1e6) all produce number values.
  • Precision limits - Safe integers range from -(2^53 - 1) to (2^53 - 1). Decimal fractions have rounding limits due to binary storage.

Number Declaration

Numbers are created by writing the literal value. For values that won't change, const is standard. Scientific notation is useful for very large or very small values - 1.5e6 is more readable than 1500000 at a glance. Hexadecimal notation appears in color values, bitmasks, and byte manipulation. The Number() function converts other types to numbers, but new Number() creates a Number object rather than a primitive - avoid the constructor form.

javascript
1// Integer and float literals
2const age = 25;
3const pi = 3.14159;
4const small = 0.0001;
5
6// Scientific notation
7const large = 1.5e6;    // 1500000
8const tiny = 2.3e-4;    // 0.00023
9
10// Other bases
11const hex = 0xA;         // 10
12const color = 0xFF5733;  // 16737843
13const octal = 0o10;      // 8
14const binary = 0b1101;   // 13
15
16console.log(large);      // 1500000
17console.log(color);      // 16737843
18
19// Number() for conversion
20const fromString = Number("42");    // 42
21const fromBool = Number(true);      // 1
22const fromNull = Number(null);      // 0
23const invalid = Number("hello");    // NaN
24
25// Avoid new Number() - creates an object, not a primitive
26// const obj = new Number(100); // [Number: 100] - not what you want

Number Formats

  • Integer literals - Whole numbers: 42, -15, 0. Stored as 64-bit float internally but behave as integers within the safe range.
  • Float literals - Decimal numbers: 3.14, -2.5. Subject to IEEE 754 rounding for fractions like 0.1.
  • Scientific notation - 1e6 is 1,000,000. 2.3e-4 is 0.00023. Useful for very large or small constants.
  • Other bases - Hex (0xFF), octal (0o17), binary (0b1010). All evaluate to regular decimal numbers.

Number Properties and Methods

The Number object has static properties for the limits of the number type and static methods for type checking and parsing. Instance methods format a number for display. The most common formatting methods are toFixed (fixed decimal places) and toPrecision (total significant digits). Both return strings, not numbers. For checking number validity, prefer Number.isNaN and Number.isFinite over the global isNaN and isFinite because the global versions coerce their argument first, which produces surprising results.

javascript
1// Static properties - the limits
2console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
3console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991
4console.log(Number.MAX_VALUE);        // 1.7976931348623157e+308
5console.log(Number.POSITIVE_INFINITY); // Infinity
6console.log(Number.NaN);              // NaN
7
8// Instance methods - formatting
9const n = 123.45678;
10console.log(n.toFixed(2));      // "123.46" - 2 decimal places
11console.log(n.toPrecision(5));  // "123.46" - 5 significant digits
12console.log(n.toString());      // "123.45678"
13console.log(n.toString(2));     // binary representation
14console.log(n.toString(16));    // hex representation
15
16// Static methods for checking
17console.log(Number.isInteger(42));    // true
18console.log(Number.isInteger(42.5));  // false
19console.log(Number.isFinite(42));     // true
20console.log(Number.isFinite(Infinity)); // false
21console.log(Number.isNaN(NaN));       // true
22console.log(Number.isNaN("text"));    // false - no coercion
23
24// vs global versions that coerce first
25console.log(isNaN("text"));           // true - coerces "text" to NaN first
26console.log(Number.isNaN("text"));    // false - no coercion
27
28// Parsing
29console.log(Number.parseInt("42px"));  // 42 - stops at non-numeric
30console.log(Number.parseFloat("3.14cm")); // 3.14

Key Number Methods

  • toFixed(n) - Returns a string with n decimal places, rounding as needed. Returns a string, not a number.
  • toPrecision(n) - Returns a string with n total significant digits.
  • Number.isNaN() and Number.isFinite() - Prefer these over global isNaN() and isFinite() - the static versions don't coerce the argument first.
  • parseInt() and parseFloat() - Parse a string until a non-numeric character is found. parseInt accepts a radix as the second argument.

Arithmetic and the Math Object

The standard arithmetic operators work as expected. The exponentiation operator ** was added in ES2016 and replaces Math.pow for most uses. The Math object provides the mathematical functions that aren't built into operators - rounding, roots, trigonometry, logarithms, random values. Math.random() returns a float between 0 (inclusive) and 1 (exclusive), which you scale and floor to get a random integer in a range.

javascript
1// Arithmetic operators
2const a = 10, b = 3;
3console.log(a + b);  // 13
4console.log(a - b);  // 7
5console.log(a * b);  // 30
6console.log(a / b);  // 3.3333...
7console.log(a % b);  // 1 (remainder)
8console.log(a ** b); // 1000 (exponentiation, ES2016)
9
10// Increment and decrement
11let count = 5;
12console.log(count++); // 5 (returns then increments)
13console.log(count);   // 6
14console.log(++count); // 7 (increments then returns)
15
16// Math object
17console.log(Math.PI);         // 3.141592653589793
18console.log(Math.sqrt(16));   // 4
19console.log(Math.round(3.7)); // 4
20console.log(Math.floor(3.9)); // 3 - rounds down
21console.log(Math.ceil(3.1));  // 4 - rounds up
22console.log(Math.abs(-5));    // 5
23console.log(Math.max(1,5,3)); // 5
24console.log(Math.min(1,5,3)); // 1
25
26// Random number in a range
27function randomInt(min, max) {
28    return Math.floor(Math.random() * (max - min + 1)) + min;
29}
30console.log(randomInt(1, 10)); // random integer 1-10

Special Number Values: NaN, Infinity, and Float Precision

Three special number situations come up often enough to know. Infinity (and -Infinity) results from dividing by zero or exceeding the number range. NaN (Not a Number) results from invalid math operations - multiplying a string by a number, taking the square root of a negative, dividing zero by zero. The NaN value is uniquely unequal to itself: NaN === NaN is false. The floating-point precision issue - 0.1 + 0.2 producing 0.30000000000000004 - is the most surprising for people coming from languages with decimal types.

javascript
1// Infinity
2console.log(1 / 0);            // Infinity
3console.log(-1 / 0);           // -Infinity
4console.log(Infinity + 1);     // Infinity
5console.log(isFinite(Infinity)); // false
6
7// NaN
8console.log(0 / 0);            // NaN
9console.log("text" * 5);       // NaN
10console.log(Math.sqrt(-1));    // NaN
11
12// NaN is not equal to itself
13console.log(NaN === NaN);      // false - unique behavior
14console.log(Number.isNaN(NaN)); // true - the correct check
15
16// Floating point precision
17console.log(0.1 + 0.2);        // 0.30000000000000004
18console.log(0.1 + 0.2 === 0.3); // false
19
20// Tolerance comparison for float equality
21function almostEqual(a, b, tolerance = 1e-10) {
22    return Math.abs(a - b) < tolerance;
23}
24console.log(almostEqual(0.1 + 0.2, 0.3)); // true
25
26// Safe integer limits
27console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
28console.log(Number.isSafeInteger(9007199254740992)); // false
29
30// Beyond safe range - use BigInt
31const big = 9007199254740993n;
32console.log(big);              // 9007199254740993n

Number Conversion and Parsing

Converting between numbers and strings is common. Number() is the strictest conversion - the entire string must be a valid number or it returns NaN. parseInt and parseFloat are lenient - they parse as many characters as make sense and stop at the first character that doesn't fit. This makes parseInt useful for strings like '42px' but also means parseInt('3.14') returns 3 because the dot isn't valid in an integer. The unary plus (+str) converts a string to a number the same way Number() does but is less readable.

javascript
1// String to number
2const str = "42";
3console.log(Number(str));    // 42 - strict
4console.log(parseInt(str));  // 42
5console.log(parseFloat(str)); // 42
6console.log(+str);           // 42 - unary plus, less readable
7
8// Parsing with non-numeric characters
9console.log(parseInt("42px"));       // 42 - stops at p
10console.log(parseFloat("3.14cm"));   // 3.14 - stops at c
11console.log(Number("42px"));         // NaN - strict
12
13// parseInt with radix
14console.log(parseInt("101", 2));   // 5 - binary to decimal
15console.log(parseInt("0xFF"));     // 255 - hex detection
16console.log(parseInt("10", 10));   // 10 - always specify 10 for decimal
17
18// Number to string
19const num = 42;
20console.log(String(num));   // "42" - explicit
21console.log(num.toString()); // "42"
22console.log(num.toString(2)); // "101010" - binary
23console.log(255..toString(16)); // "ff" - hex
24// num + "" works but looks accidental
25
26// Coercion surprises
27console.log("5" + 3);   // "53" - + with string concatenates
28console.log("5" - 3);   // 2   - - converts to number
29console.log("5" * "2"); // 10  - * converts both

Number Best Practices

Use const for numeric constants and give them descriptive names - TAX_RATE is clearer than 0.07 appearing in calculations throughout the code. For decimal equality checks, use tolerance comparisons rather than ===. Specify the radix explicitly when using parseInt - parseInt('10') works but parseInt('010') in some older engines interpreted it as octal. For financial calculations, work in integers (cents) rather than floats (dollars) to avoid accumulated rounding errors. Use Number.isNaN and Number.isFinite rather than the global versions for cleaner type checking.

javascript
1// Named constants over magic numbers
2const TAX_RATE = 0.07;
3const MAX_ITEMS = 100;
4const TIMEOUT_MS = 5000;
5
6// Tolerance for float equality
7function areEqual(a, b, tolerance = 1e-10) {
8    return Math.abs(a - b) < tolerance;
9}
10console.log(areEqual(0.1 + 0.2, 0.3)); // true
11
12// Integer math for financial calculations
13const priceInCents = 1999;   // $19.99
14const taxCents = Math.round(priceInCents * 0.07); // 140
15const totalCents = priceInCents + taxCents;        // 2139
16console.log(`$${(totalCents / 100).toFixed(2)}`);  // "$21.39"
17
18// Always specify radix with parseInt
19console.log(parseInt("10", 10));  // 10 decimal - unambiguous
20console.log(parseInt("10", 2));   // 2 binary
21console.log(parseInt("10", 16));  // 16 hex
22
23// Prefer Number.isNaN over global isNaN
24console.log(isNaN("hello"));        // true - coerces string first
25console.log(Number.isNaN("hello")); // false - no coercion, more accurate
26
27// Validity check
28function isValidNumber(v) {
29    return typeof v === 'number' && Number.isFinite(v);
30}
31console.log(isValidNumber(42));       // true
32console.log(isValidNumber(NaN));      // false
33console.log(isValidNumber(Infinity)); // false
34
35// BigInt for integers beyond safe range
36const safeMax = Number.MAX_SAFE_INTEGER;
37const tooLarge = safeMax + 1;
38console.log(Number.isSafeInteger(tooLarge)); // false
39const bigValue = BigInt(safeMax) + 1n;       // use BigInt

JavaScript Numbers FAQ

What is the number data type in JavaScript?

A single type that represents all numeric values - integers and decimals alike. All numbers are stored as 64-bit IEEE 754 double-precision floating-point values. There's no separate int or float type - 42 and 42.0 are the same thing.

Why does 0.1 + 0.2 not equal 0.3?

Because many decimal fractions can't be represented exactly in binary floating-point. 0.1 and 0.2 are stored as approximations, and those approximations don't sum to exactly 0.3. The result is 0.30000000000000004. This isn't a JavaScript bug - the same issue exists in most languages that use IEEE 754. Use tolerance comparisons (Math.abs(a - b) < 0.0001) instead of === for decimal equality checks.

What is NaN?

Not a Number - a special value produced by invalid math operations: dividing zero by zero, multiplying a string by a number, or taking the square root of a negative. NaN has the unusual property of being unequal to itself: NaN === NaN is false. Use Number.isNaN(value) to check for it.

What's the difference between Number() and parseInt()?

Number() is strict - the entire string must form a valid number or it returns NaN. parseInt() is lenient - it parses until it hits a non-numeric character and returns what it has. So Number('42px') is NaN, while parseInt('42px') is 42. Always pass the radix as the second argument to parseInt: parseInt('10', 10) to ensure decimal interpretation.

How do I generate random numbers?

Math.random() returns a float between 0 (inclusive) and 1 (exclusive). To get a random integer in a range: Math.floor(Math.random() * (max - min + 1)) + min. This is the standard pattern and gives an evenly distributed random integer from min to max inclusive.

What are safe integers?

Integers between -(2^53 - 1) and (2^53 - 1), which is roughly plus or minus 9 quadrillion. Within this range, integer arithmetic is exact. Beyond it, integers start losing precision due to the float representation. Number.isSafeInteger() checks whether a value is within this range. For larger integers, use BigInt.

When should I use BigInt?

When you need exact integer arithmetic beyond Number.MAX_SAFE_INTEGER (9007199254740991). BigInt literals end with n (9007199254740992n). BigInt and regular numbers can't be mixed in operations - you'll get a TypeError. BigInt doesn't support decimal values.

How do I format numbers with commas?

Use toLocaleString(): (1000000).toLocaleString() returns '1,000,000' in a US locale. You can specify locale and options for currency, percentages, and other formats: (1000).toLocaleString('en-US', {style: 'currency', currency: 'USD'}) returns '$1,000.00'.

What's the difference between == and === with numbers?

Always use ===. It checks both value and type without coercion. Double equals performs type coercion: 0 == false is true, '' == 0 is true, null == undefined is true. These are surprising and usually wrong. With ===, none of those are equal.

How do I check if a value is a valid number?

Number.isFinite(value) returns true for regular numbers - it returns false for NaN, Infinity, and non-number types. For a complete check: typeof value === 'number' && Number.isFinite(value). Avoid the global isFinite() which coerces its argument first.