What Is It?
A number in JavaScript is any numeric value · whole numbers, decimals, positive, negative · all treated as the same data type: Number.
This is different from many other programming languages. In Java or C++, for example, you have separate types: int (whole numbers), float (decimals), double (larger decimals), long (very big integers). JavaScript simplifies all of this into a single type: Number.
Think of JavaScript's Number like a single universal box that can hold any kind of numeric value.
Why Does This Matter?
Because there is only ONE number type in JavaScript, you don't have to decide what "kind" of number to use. But it also means JavaScript has some specific limitations and quirks you must understand to avoid bugs.
Real-World Relevance
Numbers are everywhere in programming:
- Price of a product:
29.99 - A user's age:
25 - Website visitor count:
1500000 - A GPS coordinate:
6.5244(latitude of Lagos) - A bank account balance:
250000.75 - A pixel color value:
255
Every shopping cart, calculator, grade system, or financial dashboard depends on numbers.
Integers and Decimals
JavaScript numbers can be written with or without decimal points · both are valid:
let price = 29.99; // A number WITH decimals (called a "float")
let age = 25; // A number WITHOUT decimals (called an "integer")
let temperature = -5; // Negative numbers work too
console.log(price); // 29.99
console.log(age); // 25
console.log(temperature); // -5Extra Large Numbers · Scientific (Exponential) Notation
When working with very large or very small numbers, JavaScript lets you use exponential notation (like scientific notation in math):
// e+N means "multiply by 10 to the power of N"
let bigNum = 123e5; // 123 × 10^5 = 12,300,000
let smallNum = 123e-5; // 123 × 10^-5 = 0.00123
console.log(bigNum); // 12300000
console.log(smallNum); // 0.00123Step-by-step for 123e5:
e5means "move the decimal point 5 places to the right"123→12300000
Step-by-step for 123e-5:
e-5means "move the decimal point 5 places to the LEFT"123→0.00123
Real-world use:
let earthMassKg = 5.972e24; // 5,972,000,000,000,000,000,000,000 kg
let electronCharge = 1.6e-19; // 0.00000000000000000016 coulombs
console.log(earthMassKg); // 5.972e+24The IEEE 754 Standard
JavaScript stores ALL numbers · integers and decimals alike · using the 64-bit floating-point format, defined by the international standard called IEEE 754.
Think of it like this: JavaScript uses a fixed-size container (64 bits) to store every number. These 64 bits are divided into three parts:
| Part | Bits Used | Purpose |
|---|---|---|
| Sign | 1 bit | Positive or negative |
| Exponent | 11 bits | Where the decimal point is |
| Mantissa (Fraction) | 52 bits | The actual digits |
You don't need to memorize this, but understanding the concept explains why JavaScript has precision limits · there are only 64 bits of space, so very large or very precise numbers can't be stored perfectly.
What does this mean in practice?
// JavaScript can represent integers exactly up to 15 significant digits
let safe = 999999999999999; // 15 nines → accurate
let unsafe = 9999999999999999; // 16 nines → loses accuracy!
console.log(safe); // 999999999999999 ✅ exact
console.log(unsafe); // 10000000000000000 ❌ rounded!🤔 Thinking question: What could go wrong if a bank used JavaScript numbers to store account balances in the trillions without considering this limit?
The Safe Rule
JavaScript integers are accurate up to 15 digits. Beyond that, numbers get rounded:
let x = 999999999999999; // 15 digits — safe ✅
let y = 9999999999999999; // 16 digits — NOT safe ❌
console.log(x); // 999999999999999
console.log(y); // 10000000000000000 (rounded!)Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
JavaScript has built-in constants that tell you the exact safe limits:
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991These numbers are 2^53 - 1 and -(2^53 - 1). Any integer within this range is guaranteed to be represented exactly.
// Safe:
console.log(Number.isSafeInteger(9007199254740991)); // true
// Not safe:
console.log(Number.isSafeInteger(9007199254740992)); // falseFor numbers bigger than this safe range, use BigInt (covered in Section 17).
The Classic Surprise
This is one of the most famous "gotcha" moments in JavaScript · and in many other languages that use IEEE 754:
let x = 0.1 + 0.2;
console.log(x); // 0.30000000000000004 ❌ (not 0.3!)
console.log(x === 0.3); // false ❌Why does this happen?
Just like 1/3 = 0.333... cannot be written exactly as a decimal, 0.1 and 0.2 cannot be represented EXACTLY in binary (base-2) floating-point. When you add them, the tiny imprecisions combine into a visible error.
This is NOT a JavaScript bug · it is how binary floating-point math works everywhere.
How to Fix It · Method 1: Multiply, Calculate, Divide
let x = (0.1 * 10 + 0.2 * 10) / 10;
console.log(x); // 0.3 ✅Why this works: We convert to integers first (integers are exact), do the math, then convert back.
How to Fix It · Method 2: toFixed()
let x = (0.1 + 0.2).toFixed(2);
console.log(x); // "0.30" ✅ (note: returns a STRING)
console.log(Number(x)); // 0.3 ✅ (convert back to number)⚠️ Common Beginner Mistake: Comparing floating-point numbers directly with
===. Always round or use a tolerance check for decimal comparisons.
// ❌ Wrong way:
if (0.1 + 0.2 === 0.3) { ... } // Never true!
// ✅ Right way (check if difference is tiny enough):
let result = 0.1 + 0.2;
if (Math.abs(result - 0.3) < 0.0001) {
console.log("Close enough to 0.3!");
}NaN · Not a Number
NaN stands for Not a Number. It is returned when a mathematical operation produces an undefined or invalid result.
let result = 100 / "Apple"; // Can't divide a number by text
console.log(result); // NaN
console.log(typeof result); // "number" (NaN IS of type number — a quirky fact!)How NaN spreads:
let x = NaN;
console.log(x + 5); // NaN (NaN is "infectious")
console.log(x + "5"); // "NaN5" (NaN becomes text when added to string)Checking for NaN:
Use isNaN() to test whether a value is NaN · you cannot use === NaN because NaN is the only value in JavaScript that is NOT equal to itself:
let x = NaN;
console.log(x === NaN); // false ← NaN ≠ NaN (by definition!)
console.log(isNaN(x)); // true ✅
console.log(Number.isNaN(x)); // true ✅ (more strict — preferred)Difference between isNaN() and Number.isNaN():
isNaN("hello"); // true (converts "hello" to NaN first, then checks)
Number.isNaN("hello"); // false (does NOT convert — "hello" is a string, not NaN)
Number.isNaN(NaN); // true (strictly checks for NaN)✅ Best practice: Use
Number.isNaN()for strict NaN checking.
Real-world use:
let userInput = "abc";
let num = Number(userInput);
if (Number.isNaN(num)) {
console.log("Please enter a valid number!");
} else {
console.log(`You entered: ${num}`);
}
// Output: Please enter a valid number!Infinity and -Infinity
Infinity is a special value that represents a number too large for JavaScript to hold, or the result of dividing by zero:
let x = 2 / 0;
console.log(x); // Infinity
let y = -2 / 0;
console.log(y); // -Infinity
let z = Infinity + 1;
console.log(z); // Infinity (infinity + anything = infinity)Checking for Infinity:
console.log(isFinite(Infinity)); // false
console.log(isFinite(100)); // true
console.log(isFinite(NaN)); // false
console.log(Number.isFinite(100)); // true (stricter version)Real-world use:
// Guard against division by zero in a speed calculator:
function calculateSpeed(distance, time) {
if (time === 0) return "Time cannot be zero!";
let speed = distance / time;
return isFinite(speed) ? speed : "Invalid result";
}
console.log(calculateSpeed(100, 2)); // 50
console.log(calculateSpeed(100, 0)); // "Time cannot be zero!"What Are Number Bases?
We normally count in base 10 (decimal): digits 0 · 9. But computers and programmers often use other bases:
| Base | Name | Digits Used | JS Prefix |
|---|---|---|---|
| 10 | Decimal | 0 · 9 | (none) |
| 16 | Hexadecimal | 0 · 9, A · F | 0x |
| 8 | Octal | 0 · 7 | 0o |
| 2 | Binary | 0, 1 | 0b |
Hexadecimal (Base 16)
Hexadecimal is used extensively in web development for colors, memory addresses, and encoding:
let hex1 = 0xFF; // = 255 in decimal
let hex2 = 0x1A; // = 26 in decimal
let colorRed = 0xFF0000; // red color in hex
console.log(hex1); // 255
console.log(hex2); // 26
console.log(colorRed); // 16711680In CSS and HTML, colors are written as hex:
#FF0000= red (255 red, 0 green, 0 blue)#00FF00= green#0000FF= blue
Octal (Base 8)
let oct = 0o17; // = 15 in decimal
console.log(oct); // 15Binary (Base 2)
let bin = 0b1010; // = 10 in decimal (1×8 + 0×4 + 1×2 + 0×1)
console.log(bin); // 10Converting Between Bases Using toString(base)
You can convert any number to a different base using .toString(radix):
let num = 255;
console.log(num.toString(16)); // "ff" (hexadecimal)
console.log(num.toString(8)); // "377" (octal)
console.log(num.toString(2)); // "11111111" (binary)
console.log(num.toString(10)); // "255" (decimal — default)Real-world use:
// Convert a decimal color value to hex (for CSS):
let r = 255, g = 128, b = 0;
let hexColor = `#${r.toString(16).padStart(2,"0")}${g.toString(16).padStart(2,"0")}${b.toString(16).padStart(2,"0")}`;
console.log(hexColor); // "#ff8000" (an orange color)What Is Type Coercion?
JavaScript automatically converts (coerces) data between types in certain situations. With numbers and strings, this can produce surprising results.
The + Operator · Addition OR Concatenation?
The + operator BOTH adds numbers AND joins (concatenates) strings. JavaScript decides which to do based on the types:
// Number + Number = addition:
console.log(10 + 20); // 30
// String + String = concatenation:
console.log("10" + "20"); // "1020"
// Number + String = concatenation (number gets converted to string first!):
console.log(10 + "20"); // "1020" ← TRAP!
console.log("10" + 20); // "1020" ← TRAP!⚠️ Common Beginner Mistake: Forgetting that
+with a string always concatenates. This is the #1 JavaScript bug for beginners.
// The cascade trap:
let a = 10;
let b = 20;
let c = 30;
console.log(a + b + c); // 60 (all numbers, adds normally)
console.log(a + b + "30"); // "3030" (adds 10+20=30 first, then "30"+"30"="3030")
console.log("10" + b + c); // "1020 30" — actually "102030"Step-by-step for 10 + 20 + "30":
10 + 20→30(both numbers, normal addition)30 + "30"→"3030"(number + string = concatenation)
Other Operators · Subtraction, Multiplication, Division
All other math operators (-, , /, *, %) automatically convert strings to numbers:
console.log("10" - 5); // 5 ✅ (string "10" becomes number 10)
console.log("10" * 2); // 20 ✅
console.log("10" / 2); // 5 ✅
console.log("abc" - 5); // NaN (can't convert "abc" to a number)🤔 Thinking question: If a user types
"5"in a form input, what does"5" + 3give? What about"5" - 3? Answer:"53"(concatenation) vs2(subtraction converts string to number).
toString() · Number to String
Converts a number into a string. Can optionally take a base (2, 8, 10, 16):
let x = 255;
console.log(x.toString()); // "255"
console.log(x.toString(16)); // "ff" (hexadecimal)
console.log(x.toString(2)); // "11111111" (binary)
// Works on literals too:
console.log((123).toString()); // "123"
console.log((100 + 23).toString()); // "123"toFixed(digits) · Fixed Decimal Places
Rounds a number to a specified number of decimal places and returns it as a string:
let price = 9.656;
console.log(price.toFixed(0)); // "10" (rounds to nearest integer)
console.log(price.toFixed(2)); // "9.66" (2 decimal places)
console.log(price.toFixed(4)); // "9.6560" (pads with zeros)
let small = 1.5;
console.log(small.toFixed(2)); // "1.50"Real-world use · Financial formatting:
let cartTotal = 1234.5678;
let formatted = `₦${Number(cartTotal.toFixed(2)).toLocaleString()}`;
console.log(formatted); // "₦1,234.57"⚠️ Remember:
toFixed()returns a STRING, not a number. Wrap withNumber()orparseFloat()if you need to do more math with it.
toPrecision(digits) · Significant Figures
Formats a number to a specified number of significant digits total (not just after the decimal):
let x = 9.656;
console.log(x.toPrecision()); // "9.656" (no change)
console.log(x.toPrecision(2)); // "9.7" (2 significant digits total)
console.log(x.toPrecision(4)); // "9.656" (4 significant digits total)
console.log(x.toPrecision(6)); // "9.65600" (pads to 6 sig figs)
let big = 123.456;
console.log(big.toPrecision(4)); // "123.5" (4 sig figs total)Difference between toFixed and toPrecision:
toFixed(2)→ always 2 digits AFTER the decimaltoPrecision(4)→ exactly 4 TOTAL significant digits
toExponential(digits) · Scientific Notation
Converts a number to exponential (scientific) notation:
let x = 9.656;
console.log(x.toExponential(2)); // "9.66e+0"
console.log(x.toExponential(4)); // "9.6560e+0"
let big = 123456;
console.log(big.toExponential(2)); // "1.23e+5"
console.log(big.toExponential(4)); // "1.2346e+5"valueOf() · Primitive Value
Returns the primitive number value. Rarely needed directly, but used internally by JavaScript:
let x = 123;
console.log(x.valueOf()); // 123
console.log((123).valueOf()); // 123
console.log((100 + 23).valueOf()); // 123Note: All number methods return a NEW value. They do NOT change the original number.
These are global methods (and also available on Number) that convert string values to numbers.
Number() · Convert Anything to a Number
Tries to convert a value of any type to a number:
// Strings:
console.log(Number("3.14")); // 3.14 ✅
console.log(Number("10")); // 10 ✅
console.log(Number(" 42 ")); // 42 ✅ (trims whitespace)
console.log(Number("")); // 0 ✅
console.log(Number("12 34")); // NaN ❌ (space in middle)
console.log(Number("John")); // NaN ❌
// Booleans:
console.log(Number(true)); // 1
console.log(Number(false)); // 0
// Special:
console.log(Number(null)); // 0
console.log(Number(undefined)); // NaN
console.log(Number([])); // 0
console.log(Number([10])); // 10Why does
Number(true)return 1? Becausetrueis essentially "yes" (1) andfalseis "no" (0) in mathematics. This is useful in programming logic.
parseInt() · Parse an Integer from a String
Reads a string from left to right and extracts an integer, stopping at the first non-numeric character:
console.log(parseInt("10")); // 10
console.log(parseInt("10.5")); // 10 (drops the decimal!)
console.log(parseInt("10 dogs")); // 10 (stops at space)
console.log(parseInt("dogs 10")); // NaN (starts with non-number)
console.log(parseInt("10.9abc")); // 10 (reads up to decimal)
// With a radix (base):
console.log(parseInt("FF", 16)); // 255 (hex → decimal)
console.log(parseInt("11", 2)); // 3 (binary → decimal)
console.log(parseInt("17", 8)); // 15 (octal → decimal)Step-by-step for parseInt("10 dogs"):
- Reads
1→ valid digit - Reads
0→ valid digit - Reads
(space) → stop! - Returns
10
parseFloat() · Parse a Decimal Number from a String
Like parseInt() but keeps the decimal portion:
console.log(parseFloat("10.5")); // 10.5
console.log(parseFloat("10.5abc")); // 10.5
console.log(parseFloat("10.5 20.5")); // 10.5 (only reads first number)
console.log(parseFloat("abc")); // NaN
console.log(parseFloat("3.14xyz")); // 3.14When to use which?
| Function | Use when... | Example |
|---|---|---|
Number(x) | Converting a whole value (no junk after digits) | Form input "42" → Number("42") = 42 |
parseInt(x) | You need a whole number from mixed string | "10px" → parseInt("10px") = 10 |
parseFloat(x) | You need decimal from mixed string | "3.14em" → parseFloat("3.14em") = 3.14 |
Real-world use · Extracting numbers from CSS values:
let fontSize = "16px";
let lineHeight = "1.5em";
let padding = "12.5px";
console.log(parseInt(fontSize)); // 16
console.log(parseFloat(lineHeight)); // 1.5
console.log(parseFloat(padding)); // 12.5Number.isInteger(value) · Is It a Whole Number?
Returns true if the value is an integer (no decimal part):
console.log(Number.isInteger(10)); // true
console.log(Number.isInteger(10.0)); // true (10.0 is the same as 10)
console.log(Number.isInteger(10.5)); // false
console.log(Number.isInteger("10")); // false (string, not a number)
console.log(Number.isInteger(NaN)); // false
console.log(Number.isInteger(Infinity)); // falseReal-world use:
// Validate that a quantity entered is a whole number:
function validateQuantity(qty) {
if (!Number.isInteger(qty) || qty <= 0) {
return "Quantity must be a positive whole number!";
}
return `Order quantity: ${qty}`;
}
console.log(validateQuantity(5)); // "Order quantity: 5"
console.log(validateQuantity(5.5)); // "Quantity must be a positive whole number!"Number.isFinite(value) · Is It a Real, Usable Number?
Returns true if the value is a finite number (not Infinity, not NaN):
console.log(Number.isFinite(100)); // true
console.log(Number.isFinite(Infinity)); // false
console.log(Number.isFinite(-Infinity)); // false
console.log(Number.isFinite(NaN)); // false
console.log(Number.isFinite("100")); // false (it's a string)Number.isNaN(value) · Is It Specifically NaN?
Returns true ONLY if the value is the actual NaN value:
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN("NaN")); // false (it's a string, not NaN)
console.log(Number.isNaN(undefined)); // false
console.log(Number.isNaN(100)); // falseNumber.isSafeInteger(value) · Is It Within Safe Range?
Returns true if the integer is within the safe range -(2^53 - 1) to (2^53 - 1):
console.log(Number.isSafeInteger(9007199254740991)); // true (max safe)
console.log(Number.isSafeInteger(9007199254740992)); // false (one over the limit)
console.log(Number.isSafeInteger(10.5)); // false (not an integer)Number properties are constants stored directly on the Number object. They tell you the boundaries and special values of JavaScript numbers.
Important: These must be accessed via
Number.PROPERTY_NAME· NOT via a number variable (e.g.,x.MAX_VALUEwill returnundefined).
Number.MAX_VALUE
The largest positive number JavaScript can represent:
console.log(Number.MAX_VALUE); // 1.7976931348623157e+308Numbers larger than this become Infinity.
Number.MIN_VALUE
The smallest positive number (closest to zero, NOT the most negative):
console.log(Number.MIN_VALUE); // 5e-324This is an incredibly small positive fraction · essentially the tiniest non-zero number possible.
Number.MAX_SAFE_INTEGER
The largest integer that can be represented exactly without precision loss:
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
// This equals 2^53 - 1Number.MIN_SAFE_INTEGER
The most negative safe integer:
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991
// This equals -(2^53 - 1)Number.POSITIVE_INFINITY
Represents infinity (same as the global Infinity):
console.log(Number.POSITIVE_INFINITY); // Infinity
let overflow = Number.MAX_VALUE * 2;
console.log(overflow); // InfinityNumber.NEGATIVE_INFINITY
Represents negative infinity:
console.log(Number.NEGATIVE_INFINITY); // -InfinityNumber.NaN
The NaN value (same as the global NaN):
console.log(Number.NaN); // NaN
console.log(Number.NaN === NaN); // false (NaN ≠ NaN always)Number.EPSILON
The smallest difference between two representable floating-point numbers:
console.log(Number.EPSILON); // 2.220446049250313e-16Used to safely compare floating-point numbers:
// Safe float comparison using EPSILON:
function areEqual(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
console.log(areEqual(0.1 + 0.2, 0.3)); // true ✅Number Instance Methods
| Method | What It Does | Example → Output |
|---|---|---|
toFixed(n) | Format to n decimal places (returns string) | (9.656).toFixed(2) → "9.66" |
toPrecision(n) | Format to n total significant digits | (9.656).toPrecision(4) → "9.656" |
toExponential(n) | Convert to exponential notation | (123456).toExponential(2) → "1.23e+5" |
toString(base) | Convert number to string (optional base) | (255).toString(16) → "ff" |
valueOf() | Return the primitive number value | (123).valueOf() → 123 |
Number Static Methods
| Method | What It Does | Example → Output |
|---|---|---|
Number(x) | Convert value to number | Number("3.14") → 3.14 |
Number.isInteger(x) | Is it a whole number? | Number.isInteger(10.5) → false |
Number.isFinite(x) | Is it a finite number? | Number.isFinite(Infinity) → false |
Number.isNaN(x) | Is it NaN specifically? | Number.isNaN(NaN) → true |
Number.isSafeInteger(x) | Is it in the safe range? | Number.isSafeInteger(9007199254740992) → false |
Number.parseFloat(x) | Parse string to float | Number.parseFloat("3.14em") → 3.14 |
Number.parseInt(x) | Parse string to integer | Number.parseInt("10px") → 10 |
Number Properties
| Property | Value | Meaning |
|---|---|---|
Number.MAX_VALUE | ~1.8e+308 | Largest representable number |
Number.MIN_VALUE | ~5e-324 | Smallest positive number |
Number.MAX_SAFE_INTEGER | 9007199254740991 | Largest exact integer |
Number.MIN_SAFE_INTEGER | -9007199254740991 | Smallest exact integer |
Number.POSITIVE_INFINITY | Infinity | Overflow value |
Number.NEGATIVE_INFINITY | -Infinity | Negative overflow |
Number.NaN | NaN | Not a Number |
Number.EPSILON | 2.22e-16 | Smallest float difference |
Global Functions
| Function | What It Does | Example → Output |
|---|---|---|
isNaN(x) | Checks if x is NaN (with type coercion) | isNaN("hello") → true |
isFinite(x) | Checks if x is finite (with type coercion) | isFinite(Infinity) → false |
parseInt(x, base) | Parse integer from string | parseInt("FF", 16) → 255 |
parseFloat(x) | Parse float from string | parseFloat("3.14abc") → 3.14 |
What Are Bitwise Operators?
Bitwise operators work directly on the binary (base-2) representation of numbers · operating on individual bits (0s and 1s), one at a time.
Think of it this way: when you add 5 + 3, you work on the full decimal numbers. But when you do 5 & 3 (bitwise AND), JavaScript first converts both numbers to their binary form, performs the operation bit-by-bit, then converts the result back to decimal.
How JavaScript Handles Bitwise Operations
JavaScript stores numbers as 64-bit floats, but all bitwise operations work on 32-bit signed integers. This means:
- The number is converted to a 32-bit binary integer
- The bitwise operation is performed
- The result is converted back to a 64-bit JavaScript number
Number 5 in binary (32-bit): 00000000000000000000000000000101
Number 3 in binary (32-bit): 00000000000000000000000000000011Why Learn Bitwise?
Bitwise operators are used in:
- Permissions systems (e.g., read = 4, write = 2, execute = 1 · like Linux file permissions)
- Image processing (pixel color manipulation)
- Cryptography and hashing algorithms
- Performance-critical code (bitwise ops are extremely fast)
- Checking even/odd numbers quickly
- Network programming (IP address masking)
Bitwise AND · &
Returns 1 in each bit position where BOTH corresponding bits are 1, otherwise 0.
Truth table for AND:
| Bit A | Bit B | A AND B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
// 5 in binary: 0101
// 3 in binary: 0011
// -----
// 5 & 3: 0001 = 1
console.log(5 & 3); // 1
// How each bit was decided:
// bit 3: 0 AND 0 = 0
// bit 2: 1 AND 0 = 0
// bit 1: 0 AND 1 = 0
// bit 0: 1 AND 1 = 1 ← only this bit is 1 in BOTHReal-world trick · Check if a number is even or odd:
// The last bit of any number is 1 if odd, 0 if even
console.log(7 & 1); // 1 → odd (7 = 0111, last bit = 1)
console.log(8 & 1); // 0 → even (8 = 1000, last bit = 0)
console.log(100 & 1); // 0 → even
console.log(99 & 1); // 1 → odd
// Much faster than using modulo:
function isEven(n) { return (n & 1) === 0; }
console.log(isEven(10)); // true
console.log(isEven(11)); // falseBitwise OR · |
Returns 1 in each bit position where AT LEAST ONE of the corresponding bits is 1.
Truth table for OR:
| Bit A | Bit B | A OR B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
// 5 = 0101
// 3 = 0011
// | = 0111 = 7
console.log(5 | 3); // 7Real-world use · Permissions (like Linux):
const READ = 4; // 100 in binary
const WRITE = 2; // 010 in binary
const EXECUTE = 1; // 001 in binary
// Give read + write permission using OR:
let userPermissions = READ | WRITE;
console.log(userPermissions); // 6 (110 in binary)
// Check if user has write permission using AND:
console.log(userPermissions & WRITE); // 2 → truthy → has write!
console.log(userPermissions & EXECUTE); // 0 → falsy → no executeBitwise XOR · ^
Returns 1 where bits are DIFFERENT, 0 where they are the SAME. ("Exclusive OR")
Truth table for XOR:
| Bit A | Bit B | A XOR B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
// 5 = 0101
// 3 = 0011
// ^ = 0110 = 6
console.log(5 ^ 3); // 6
// XOR trick — toggle a bit:
let flag = 0;
flag ^= 1; console.log(flag); // 1 (toggled on)
flag ^= 1; console.log(flag); // 0 (toggled off)
flag ^= 1; console.log(flag); // 1 (toggled on again)Bitwise NOT · ~
Flips ALL bits (0 becomes 1, 1 becomes 0). Because of how JavaScript handles 32-bit signed integers, ~n always equals -(n + 1):
console.log(~5); // -6 (-(5+1) = -6)
console.log(~0); // -1
console.log(~-1); // 0
// Binary:
// 5 in 32-bit: 00000000000000000000000000000101
// ~5: 11111111111111111111111111111010 = -6 (two's complement)Why -(n+1)? JavaScript uses "two's complement" for negative numbers. Flipping all bits of n gives the two's complement value of -(n+1).
Common use · ~ as a quick indexOf check:
let arr = [10, 20, 30];
// Old way:
if (arr.indexOf(20) !== -1) { console.log("found"); }
// Shortcut with ~ (if index is -1, ~(-1) = 0 which is falsy):
if (~arr.indexOf(20)) { console.log("found"); } // "found"
if (~arr.indexOf(99)) { console.log("found"); } // (nothing printed — 0 is falsy)Left Shift · << (Zero Fill)
Shifts bits to the LEFT by the specified number of positions. Zeros fill in from the right. Left bits fall off.
Effect: Multiplies the number by 2 for each shift position.
// 5 in binary: 0101
// 5 << 1: 1010 = 10 (shifted 1 left = multiplied by 2^1 = ×2)
// 5 << 2: 10100 = 20 (shifted 2 left = multiplied by 2^2 = ×4)
console.log(5 << 1); // 10
console.log(5 << 2); // 20
console.log(5 << 3); // 40
console.log(1 << 4); // 16 (very fast way to compute 2^4)Step-by-step for 5 << 1:
5 in binary: 0 0 0 0 0 1 0 1
Shift left 1: 0 0 0 0 1 0 1 0 ← zero pushed in from right
Result: 10 in decimalSigned Right Shift · >> (Sign-Preserving)
Shifts bits to the RIGHT. The leftmost bit (sign bit) is copied to fill from the left. Right bits fall off.
Effect: Divides the number by 2 for each shift position (rounds toward negative infinity).
console.log(20 >> 1); // 10 (20 / 2 = 10)
console.log(20 >> 2); // 5 (20 / 4 = 5)
console.log(-20 >> 1); // -10 (sign is preserved: -20 / 2 = -10)Unsigned Right Shift · >>> (Zero Fill)
Like >> but always fills with zeros from the left (ignores sign). For negative numbers, this gives a large positive number:
console.log(20 >>> 1); // 10 (same as >> for positive numbers)
console.log(-1 >>> 0); // 4294967295 (all 32 bits become 1 = max 32-bit unsigned int)Practical use of >>> 0 · Convert to 32-bit unsigned integer:
// Useful for converting any number to a safe 32-bit integer:
function to32bit(n) { return n >>> 0; }
console.log(to32bit(-1)); // 4294967295
console.log(to32bit(5)); // 5Bitwise Assignment Operators
All bitwise operators have assignment shorthand versions:
let x = 5;
x &= 3; console.log(x); // 1 (same as x = x & 3)
x = 5;
x |= 3; console.log(x); // 7 (same as x = x | 3)
x = 5;
x ^= 3; console.log(x); // 6 (same as x = x ^ 3)
x = 5;
x <<= 1; console.log(x); // 10 (same as x = x << 1)
x = 5;
x >>= 1; console.log(x); // 2 (same as x = x >> 1)Full Bitwise Operator Reference
| Operator | Name | Usage | Example | ||
|---|---|---|---|---|---|
& | AND | 1 only if both bits are 1 | 5 & 3 → 1 | ||
| `\ | ` | OR | 1 if at least one bit is 1 | `5 \ | 3 → 7` |
^ | XOR | 1 if bits are different | 5 ^ 3 → 6 | ||
~ | NOT | Flip all bits | ~5 → -6 | ||
<< | Left shift | Shift left (pad with 0) | 5 << 1 → 10 | ||
>> | Right shift | Shift right (preserve sign) | 20 >> 1 → 10 | ||
>>> | Unsigned right shift | Shift right (pad with 0) | 20 >>> 1 → 10 |
Decimal to Binary
function dec2bin(dec) {
return (dec >>> 0).toString(2);
}
console.log(dec2bin(5)); // "101"
console.log(dec2bin(255)); // "11111111"
console.log(dec2bin(10)); // "1010"How it works:
>>> 0converts the number to an unsigned 32-bit integer (handles negatives).toString(2)converts to base-2 (binary) string
Binary to Decimal
function bin2dec(bin) {
return parseInt(bin, 2).toString(10);
}
console.log(bin2dec("101")); // "5"
console.log(bin2dec("11111111")); // "255"
console.log(bin2dec("1010")); // "10"How it works:
parseInt(bin, 2)parses the string as base-2 (binary).toString(10)converts back to decimal string (base 10)
Two's Complement · How Negative Numbers Are Stored
JavaScript binary numbers are stored in two's complement format. This means:
- Positive numbers: stored normally
- Negative numbers: the bitwise NOT of the number, plus 1
// 5 in binary: 00000000000000000000000000000101
// NOT 5 (~5): 11111111111111111111111111111010
// ~5 + 1 = -5: 11111111111111111111111111111011
// Verify:
console.log(~5 + 1); // -5 ← this is two's complement of 5What Is BigInt?
BigInt is JavaScript's second numeric data type (introduced in ES2020), designed specifically to handle integers that are too large for the regular Number type to represent accurately.
Remember: regular numbers lose precision beyond 15-16 digits. BigInt has NO upper limit · it can handle numbers of arbitrary size.
When Do You Need BigInt?
- Bank account systems with very high balances
- Cryptography (RSA keys are thousands of bits long)
- Scientific computing (galaxy-sized numbers)
- Unique ID generators for large databases
- Handling timestamps in nanoseconds
Creating a BigInt
Two ways to create a BigInt:
Method 1 · Append n to any integer:
let big1 = 9999999999999999n; // note the 'n' at the end
let big2 = 1234567890123456789012345n;
console.log(big1); // 9999999999999999n ✅ (exact! no rounding)Compare to regular Number:
let regular = 9999999999999999; // number (no n)
let bigint = 9999999999999999n; // BigInt (with n)
console.log(regular); // 10000000000000000 ❌ rounded!
console.log(bigint); // 9999999999999999n ✅ exact!Method 2 · Use BigInt() constructor:
let big3 = BigInt(1234567890123456789);
let big4 = BigInt("1234567890123456789012345"); // use string for very large!
console.log(big3); // 1234567890123456768n (not exact — limit of JS number literal)
console.log(big4); // 1234567890123456789012345n ✅ (string bypasses number limit)⚠️ Important: When creating VERY large BigInts with the
BigInt()constructor, always pass a string to avoid the number precision issue being baked in before BigInt even sees it.
BigInt Arithmetic
BigInt supports all standard math operators, but ONLY with other BigInts:
let a = 1000000000000000000n;
let b = 2000000000000000000n;
console.log(a + b); // 3000000000000000000n ✅
console.log(b - a); // 1000000000000000000n ✅
console.log(a * 3n); // 3000000000000000000n ✅
console.log(b / a); // 2n ✅
console.log(b % 3n); // 2n ✅ (remainder)
console.log(2n ** 100n); // 1267650600228229401496703205376n ✅ (2^100)⚠️ You CANNOT mix BigInt and Number in operations:
let big = 10n;
let num = 5;
// ❌ This throws a TypeError:
// console.log(big + num); // TypeError: Cannot mix BigInt and other types
// ✅ Convert explicitly:
console.log(big + BigInt(num)); // 15n
console.log(Number(big) + num); // 15 (converts BigInt to Number first)BigInt Division · Always Integer Result
BigInt division truncates (cuts off) the decimal, never rounds:
console.log(10n / 3n); // 3n (NOT 3.333...n — decimals not allowed!)
console.log(-10n / 3n); // -3n (truncates toward zero)BigInt cannot represent decimals at all. For decimal math, use regular
Number.
BigInt Comparisons
// Comparing two BigInts:
console.log(10n > 5n); // true
console.log(10n < 5n); // false
console.log(10n === 10n); // true
// Comparing BigInt to Number:
console.log(10n == 10); // true (loose equality — converts types)
console.log(10n === 10); // false (strict equality — different types!)
console.log(10n > 9); // true (comparison operators work across types)
console.log(10n < 20); // trueRule:
==works between BigInt and Number.===always returnsfalsebecause they are different types.
BigInt as Boolean
BigInt behaves like a number in boolean context: 0n is falsy, everything else is truthy:
console.log(Boolean(0n)); // false
console.log(Boolean(1n)); // true
console.log(Boolean(-5n)); // true
if (0n) { console.log("never"); }
if (5n) { console.log("yes!"); } // "yes!"BigInt Number Bases
BigInt supports hex, octal, and binary notation just like regular numbers:
let hexBig = 0x1FFFFFFFFFFFFFn; // hex BigInt
let octBig = 0o777777777777777n; // octal BigInt
let binBig = 0b111111111111111111111111111111111111111111111111111n; // binary BigInt
console.log(hexBig); // 9007199254740991nBigInt Methods
let big = 12345678901234567890n;
console.log(big.toString()); // "12345678901234567890"
console.log(big.toString(16)); // hex string
console.log(big.toString(2)); // binary string
console.log(typeof big); // "bigint"BigInt Bitwise Operations
BigInt supports bitwise operators, but ONLY with other BigInts:
let a = 5n; // binary: 0101
let b = 3n; // binary: 0011
console.log(a & b); // 1n (0001) AND
console.log(a | b); // 7n (0111) OR
console.log(a ^ b); // 6n (0110) XOR
console.log(~a); // -6n NOT
// Shifts:
let big = 10n; // binary: 1010
console.log(big << 2n); // 40n (101000) — left shift by 2
console.log(big >> 1n); // 5n (0101) — right shift by 1
// Note: Unsigned right shift (>>>) is NOT allowed with BigIntBigInt Limitations
Things BigInt CANNOT do:
- Use
Mathmethods:Math.max(1n, 2n)→ TypeError - Mix with regular Number in math operations
- Represent decimal/fractional values
- Use unsigned right shift
>>>
// ❌ These will throw errors:
// Math.max(1n, 2n); // TypeError
// 10n + 5; // TypeError
// 10n / 3n === 3.33n; // BigInt can't be decimal
// ✅ Workarounds:
console.log(10n > 9n ? 10n : 9n); // 10n (manual comparison instead of Math.max)
console.log(Number(10n) + 5); // 15 (convert to number first)BigInt is the Second Numeric Type
console.log(typeof 42); // "number"
console.log(typeof 42n); // "bigint" ← a completely different type🏋️ Exercise 1 · Smart Price Calculator
Objective: Practice toFixed(), floating-point precision fixes, Number(), parseFloat(), and isNaN().
Real-world Scenario: You're building an e-commerce checkout system that must handle user input, calculate totals accurately, and display formatted prices.
Warm-up Mini-Example:
let price = "24.99"; // user input is a string from form
let qty = 3;
let total = parseFloat(price) * qty;
console.log(`Total: $${total.toFixed(2)}`); // Total: $74.97Your Exercise:
Given this data from a web form:
let rawPrice = "1499.99"; // price entered as string
let rawQty = "3"; // quantity entered as string
let discountRate = "0.15"; // 15% discount
let taxRate = 0.075; // 7.5% taxStep-by-step Instructions:
- Convert all string inputs to numbers using
parseFloat()orNumber() - Validate: if any value is
NaN, display an error message - Calculate: subtotal = price × quantity
- Apply discount: discounted = subtotal × (1 - discountRate)
- Apply tax: total = discounted × (1 + taxRate)
- Display each value formatted to 2 decimal places
Expected Output:
=== Order Summary ===
Unit Price: ₦1,499.99
Quantity: 3
Subtotal: ₦4,499.97
Discount (15%): -₦675.00
After Discount: ₦3,824.97
Tax (7.5%): ₦286.87
TOTAL: ₦4,111.84Self-Check Questions:
- What does
parseFloat("1499.99abc")return? - What happens if the user types
"free"as the price? - Why not just use
Number()instead ofparseFloat()for currency?
What-if challenge: What if qty is 0 or negative? Add a validation check.
🏋️ Exercise 2 · Binary Permissions Manager
Objective: Practice bitwise operators in a real-world permissions system.
Real-world Scenario: A content management system (CMS) needs to manage user permissions. Each permission is a power of 2 (bit flag), allowing combinations to be stored as a single number.
Warm-up Mini-Example:
const READ = 1; // 001
const WRITE = 2; // 010
let perm = READ | WRITE; // 011 = 3
console.log("Can read:", !!(perm & READ)); // true
console.log("Can write:", !!(perm & WRITE)); // trueYour Exercise:
const PERMISSIONS = {
VIEW: 1, // 0001
COMMENT: 2, // 0010
EDIT: 4, // 0100
DELETE: 8, // 1000
ADMIN: 16 // 10000
};Step-by-step Instructions:
- Create three users with different permission levels:
- guest: VIEW only - editor: VIEW, COMMENT, EDIT - admin: all permissions
- Write a function
hasPermission(userPerms, perm)that returnstrue/false - Write a function
grantPermission(userPerms, perm)that adds a permission - Write a function
revokePermission(userPerms, perm)that removes a permission - Test all functions and display the results
Hints:
- Grant permission:
userPerms | newPerm(OR adds it) - Revoke permission:
userPerms & ~removedPerm(AND NOT removes it) - Check permission:
(userPerms & perm) !== 0
Expected Output:
=== Permission Manager ===
Guest permissions (binary): 00001
Editor permissions (binary): 00111
Admin permissions (binary): 11111
Guest can VIEW: true
Guest can EDIT: false
Editor can EDIT: true
Editor can DELETE: false
Admin can DELETE: true
Granting COMMENT to guest...
New guest permissions: 00011
Guest can now COMMENT: true
Revoking EDIT from editor...
New editor permissions: 00011
Editor can EDIT: false🏋️ Exercise 3 · BigInt Factorial Calculator
Objective: Practice BigInt for large number computation.
Real-world Scenario: In cryptography and statistics, you often need to calculate factorials of large numbers. These numbers become enormous very quickly · far beyond what regular Number can handle.
Warm-up Mini-Example:
// 5! = 5 × 4 × 3 × 2 × 1 = 120
function factorial(n) {
let result = 1n;
for (let i = 2n; i <= BigInt(n); i++) {
result *= i;
}
return result;
}
console.log(factorial(5)); // 120n
console.log(factorial(10)); // 3628800nYour Exercise:
- Write a
factorial(n)function using BigInt - Calculate
factorial(50)andfactorial(100) - Show the number of digits in the result using
.toString().length - Show why regular Number fails for
factorial(20)(compare results) - Display results formatted with digit grouping
Expected Output:
=== Factorial Calculator ===
factorial(10) = 3,628,800
Digits: 7
factorial(20) using Number: 2432902008176640000 ❌ (imprecise)
factorial(20) using BigInt: 2432902008176640000n ✅ (exact)
factorial(50) = 30,414,093,201,713,378,043,612,608,166,979,...
Digits: 65
factorial(100):
Digits: 158
(Too large to display fully!)💰 Project Overview
You will build a Personal Finance Dashboard · a realistic program that calculates loan repayments, investment growth, currency conversion, and savings goals, using all the number concepts from this tutorial.
Skills practiced: toFixed(), toPrecision(), parseFloat(), isNaN(), Number.isFinite(), floating-point precision handling, BigInt for large sums.
Stage 1 · Loan Repayment Calculator
Illustrative Preview:
// Monthly interest rate
let monthlyRate = 0.12 / 12; // 12% annual → 1% monthly
console.log(monthlyRate.toPrecision(4)); // "0.01000"Lesson 08 complete! 🎉
You covered:
- ✅ 1. What Is a Number in JavaScript?
- ✅ 2. Writing Numbers
- ✅ 3. How JavaScript Stores Numbers · The 64-bit Float Format
- ✅ 4. Integer Precision · The 15-Digit Limit
- ✅ 5. Floating-Point Precision Problems (and How to Fix Them)
- ✅ 6. Special Number Values: NaN, Infinity, and -Infinity
- ✅ 7. Number Bases: Hexadecimal, Octal, Binary
- ✅ 8. Numbers and Strings Together · Type Coercion Traps