Chapter 12: R Numbers
R Numbers — basically everything you need to know about how R handles numeric values (numbers).
In R, “numbers” are not just one thing. There are two main kinds of numbers, plus special values like infinity or missing data. Understanding this deeply saves you from a lot of silent bugs later (especially with decimals, large datasets, or calculations).
I’m going to explain like your patient offline teacher — slow, with lots of real examples you can copy-paste right now into RStudio, why things behave this way, common mistakes, and 2026 best practices.
1. The Two Main Types of Numbers in R
R has two atomic numeric types:
| Type | Internal name (typeof()) | What it is | Default? | Example creation | Memory use | When to use it intentionally |
|---|---|---|---|---|---|---|
| double | “double” | Floating-point real numbers (decimals) | Yes | 3.14, 1, c(28.5, 30.2) | 8 bytes | Almost always (default) |
| integer | “integer” | Whole numbers only | No | 42L, 1:10, as.integer(5) | 4 bytes | Big datasets, counts, indices |
Key fact most beginners miss: Even when you write 5 or 100, R stores it as double (not integer)!
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
x <- 5 typeof(x) # "double" class(x) # "numeric" ← what most functions see y <- 5L typeof(y) # "integer" class(y) # "integer" |
2. Why Two Types? (Practical Differences)
- Double is the default → most math functions expect it
- Integer saves memory (half the size) → useful for huge vectors (millions of rows)
- Double allows decimals + special values (Inf, NaN)
- Integer has only one special value: NA_integer_
Examples:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
# Double can have decimals + infinity temps_hyd <- c(28.5, 29.8, 30.2, Inf, -Inf) typeof(temps_hyd) # "double" # Integer cannot have decimals counts <- c(1L, 2L, 3L) # counts <- c(1L, 2.5L) # Error! Cannot mix integer + double decimal |
3. Creating Numbers – Hands-on Examples
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# Plain number → double pi_approx <- 3.14159 whole <- 2026 typeof(whole) # "double" ← surprise! # Force integer year <- 2026L days <- 1:31 # special shortcut → integer vector ages <- as.integer(c(22, 25, 19)) typeof(ages) # "integer" # From other types as.integer(TRUE) # 1 as.integer(FALSE) # 0 as.double("3.14") # 3.14 as.integer("3.14") # 3 (truncates decimal!) as.integer("hello")# NA with warning: NAs introduced by coercion |
4. Special Numeric Values (Very Important!)
Doubles have four special values — integers have only NA_integer_
| Value | Meaning | How it appears | Example how it happens |
|---|---|---|---|
| NA | Missing / unknown value | NA_real_ or NA_integer_ | Data not collected, error in entry |
| NaN | Not a Number (undefined math) | NaN | 0/0 , Inf – Inf , sqrt(-1) |
| Inf | Positive infinity | Inf | 1/0 , exp(1000) |
| -Inf | Negative infinity | -Inf | -1/0 , log(0) |
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# Examples 1 / 0 # Inf -1 / 0 # -Inf 0 / 0 # NaN Inf - Inf # NaN sqrt(-1) # NaN + warning # Missing student_marks <- c(85, NA, 92, NA_real_) mean(student_marks) # NA mean(student_marks, na.rm = TRUE) # 88.5 ← very common! # Check them is.na(NA) # TRUE is.nan(NaN) # TRUE is.infinite(Inf) # TRUE is.finite(3.14) # TRUE |
5. Coercion / Automatic Conversion (The Silent Trap)
When you mix types in a vector, R coerces to the most flexible type:
logical < integer < double < complex < character
|
0 1 2 3 4 5 6 7 8 |
c(TRUE, 5L) # TRUE → 1L → integer c(5L, 3.14) # 5L → 5 → double c(3.14, "Hyderabad") # everything → character |
Very common bug:
|
0 1 2 3 4 5 6 7 |
scores <- c(92, 85, "absent", 78) # "absent" makes whole vector character! mean(scores) # Error: argument is not numeric or logical |
Fix: Clean data first or use na_if() / lists / data frames for mixed types.
6. Precision & Comparison Warnings (Floating-Point Reality)
Doubles are approximations — never trust exact equality:
|
0 1 2 3 4 5 6 7 8 9 10 |
0.1 + 0.2 == 0.3 # FALSE ! (classic floating-point issue) 0.1 + 0.2 # 0.30000000000000004 # Safe way dplyr::near(0.1 + 0.2, 0.3) # TRUE ← use this instead of == |
7. Your Mini Practice Right Now (Copy → Run!)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# Different number styles a <- 29.8 # double b <- 30L # integer c <- Inf d <- NA_real_ e <- NaN # Check types & specials data.frame( name = letters[1:5], value = c(a, b, c, d, e), typeof = sapply(list(a,b,c,d,e), typeof), is_finite = is.finite(c(a,b,c,d,e)), is_na = is.na(c(a,b,c,d,e)) ) # Safe average with missing + infinity temps <- c(28.5, 29.8, Inf, NA, 30.2) mean(temps, na.rm = TRUE, trim = 0.2) # trim ignores extremes |
Summary Cheat-Sheet (Keep This!)
- Most numbers → double (even 5 or 2026)
- Want integer → add L or use as.integer()
- Specials: NA (missing), NaN (undefined), Inf/-Inf
- Coercion: doubles win over integers
- Compare floats → use near() not ==
- Always use na.rm = TRUE in summaries
Feeling good?
Next step?
- Deeper into vectors (since numbers live in vectors)?
- How numbers behave in data frames / dplyr?
- Or any line confusing you right now?
Just tell me — whiteboard is ready! ☕🚀
