Lua - Numbers
Overview
Estimated time: 25–30 minutes
Numbers are fundamental to programming and Lua provides robust numeric capabilities. Lua has a unified number type that can represent both integers and floating-point numbers, along with a comprehensive math library for advanced mathematical operations.
Learning Objectives
- Understand Lua's number system and number types
- Master basic arithmetic and mathematical operations
- Explore the math library and advanced functions
- Learn number formatting and conversion techniques
- Apply numerical computing in practical scenarios
Prerequisites
- Understanding of Lua operators and expressions
- Knowledge of variables and functions
Lua Number System
Lua has a single number type that can represent both integers and floating-point numbers:
-- Integer literals
local integer1 = 42
local integer2 = -17
local zero = 0
-- Floating-point literals
local float1 = 3.14159
local float2 = -2.5
local scientific = 1.23e-4 -- Scientific notation
local scientific2 = 5.67E+2 -- 567.0
-- Different number bases (Lua 5.2+)
local decimal = 255
local hexadecimal = 0xFF -- Same as 255
local binary = 0b11111111 -- Same as 255 (Lua 5.4+)
print("Integer:", integer1, type(integer1))
print("Float:", float1, type(float1))
print("Scientific:", scientific, type(scientific))
print("Hex 0xFF:", hexadecimal)
print("Binary 0b11111111:", binary)
-- Check if a number is an integer (Lua 5.3+)
print("Is 42 an integer?", math.type(42))
print("Is 3.14 an integer?", math.type(3.14))
print("Is 5.0 an integer?", math.type(5.0))
Expected Output:
Integer: 42 number
Float: 3.14159 number
Scientific: 0.000123 number
Hex 0xFF: 255
Binary 0b11111111: 255
Is 42 an integer? integer
Is 3.14 an integer? float
Is 5.0 an integer? float
Basic Arithmetic Operations
Lua supports all standard arithmetic operations:
local a = 15
local b = 4
print("Basic arithmetic operations:")
print("a + b =", a + b) -- Addition: 19
print("a - b =", a - b) -- Subtraction: 11
print("a * b =", a * b) -- Multiplication: 60
print("a / b =", a / b) -- Division: 3.75
print("a // b =", a // b) -- Floor division: 3
print("a % b =", a % b) -- Modulo: 3
print("a ^ b =", a ^ b) -- Exponentiation: 50625
-- Unary operations
print("-a =", -a) -- Unary minus: -15
-- Mixed integer and float operations
local int_val = 10
local float_val = 3.5
print("Mixed operations:")
print("10 + 3.5 =", int_val + float_val) -- 13.5
print("10 * 3.5 =", int_val * float_val) -- 35.0
print("10 / 3.5 =", int_val / float_val) -- 2.857...
Expected Output:
Basic arithmetic operations:
a + b = 19
a - b = 11
a * b = 60
a / b = 3.75
a // b = 3
a % b = 3
a ^ b = 50625
-a = -15
Mixed operations:
10 + 3.5 = 13.5
10 * 3.5 = 35.0
10 / 3.5 = 2.8571428571429
Number Conversion
Converting between numbers and other types:
-- String to number conversion
local str_num = "123"
local str_float = "45.67"
local str_invalid = "abc"
print("String to number conversions:")
print("tonumber('123'):", tonumber(str_num))
print("tonumber('45.67'):", tonumber(str_float))
print("tonumber('abc'):", tonumber(str_invalid)) -- Returns nil
-- With different bases
print("tonumber('FF', 16):", tonumber("FF", 16)) -- Hex to decimal
print("tonumber('1010', 2):", tonumber("1010", 2)) -- Binary to decimal
print("tonumber('77', 8):", tonumber("77", 8)) -- Octal to decimal
-- Number to string conversion
local num = 42.5
print("Number to string:")
print("tostring(42.5):", tostring(num))
print("Concatenation:", "Value: " .. num)
-- Safe conversion function
local function safe_tonumber(str, default)
local num = tonumber(str)
return num or default or 0
end
print("Safe conversions:")
print("safe_tonumber('123'):", safe_tonumber("123"))
print("safe_tonumber('abc', -1):", safe_tonumber("abc", -1))
print("safe_tonumber('xyz'):", safe_tonumber("xyz"))
Expected Output:
String to number conversions:
tonumber('123'): 123
tonumber('45.67'): 45.67
tonumber('abc'): nil
tonumber('FF', 16): 255
tonumber('1010', 2): 10
tonumber('77', 8): 63
Number to string:
tostring(42.5): 42.5
Concatenation: Value: 42.5
Safe conversions:
safe_tonumber('123'): 123
safe_tonumber('abc', -1): -1
safe_tonumber('xyz'): 0
Math Library
Lua's math library provides advanced mathematical functions:
Constants and Basic Functions
-- Mathematical constants
print("Math constants:")
print("π (pi):", math.pi)
print("Infinity:", math.huge)
print("Smallest normal positive double:", math.mininteger)
print("Largest representable integer:", math.maxinteger)
-- Basic math functions
local x = -3.7
print("\nBasic functions for x =", x)
print("Absolute value:", math.abs(x))
print("Floor (round down):", math.floor(x))
print("Ceiling (round up):", math.ceil(x))
print("Sign:", x > 0 and 1 or x < 0 and -1 or 0)
-- Rounding to nearest integer
local function round(num)
return math.floor(num + 0.5)
end
local numbers = {3.2, 3.7, -2.3, -2.8}
print("\nRounding examples:")
for _, num in ipairs(numbers) do
print(string.format("%.1f -> floor: %d, ceil: %d, round: %d",
num, math.floor(num), math.ceil(num), round(num)))
end
Expected Output:
Math constants:
π (pi): 3.1415926535898
Infinity: inf
Smallest normal positive double: -9223372036854775808
Largest representable integer: 9223372036854775807
Basic functions for x = -3.7
Absolute value: 3.7
Floor (round down): -4
Ceiling (round up): -3
Sign: -1
Rounding examples:
3.2 -> floor: 3, ceil: 4, round: 3
3.7 -> floor: 3, ceil: 4, round: 4
-2.3 -> floor: -3, ceil: -2, round: -2
-2.8 -> floor: -3, ceil: -2, round: -3
Power and Root Functions
-- Power and exponential functions
local base = 2
local exponent = 3
print("Power functions:")
print("2^3 (using ^):", base ^ exponent)
print("2^3 (using math.pow):", math.pow(base, exponent)) -- Deprecated in Lua 5.3+
print("Square root of 16:", math.sqrt(16))
print("Cube root of 27:", 27 ^ (1/3))
-- Exponential and logarithm functions
local e_value = math.exp(1) -- e^1
print("\nExponential and logarithms:")
print("e (Euler's number):", e_value)
print("e^2:", math.exp(2))
print("Natural log of e:", math.log(e_value))
print("Log base 10 of 100:", math.log(100, 10)) -- Lua 5.2+
print("Log base 2 of 8:", math.log(8, 2))
-- Practical example: compound interest
local function compound_interest(principal, rate, time, compounds_per_year)
compounds_per_year = compounds_per_year or 1
return principal * ((1 + rate / compounds_per_year) ^ (compounds_per_year * time))
end
local principal = 1000
local annual_rate = 0.05 -- 5%
local years = 10
print("\nCompound interest calculation:")
print(string.format("Principal: $%.2f", principal))
print(string.format("Annual rate: %.1f%%", annual_rate * 100))
print(string.format("Time: %d years", years))
print(string.format("Final amount: $%.2f", compound_interest(principal, annual_rate, years)))
Expected Output:
Power functions:
2^3 (using ^): 8
2^3 (using math.pow): 8
Square root of 16: 4
Cube root of 27: 3
Exponential and logarithms:
e (Euler's number): 2.718281828459
e^2: 7.3890560989307
Natural log of e: 1
Log base 10 of 100: 2
Log base 2 of 8: 3
Compound interest calculation:
Principal: $1000.00
Annual rate: 5.0%
Time: 10 years
Final amount: $1628.89
Trigonometric Functions
-- Trigonometric functions (work with radians)
local degrees = 45
local radians = math.rad(degrees) -- Convert degrees to radians
print("Trigonometric functions:")
print(string.format("45° = %.4f radians", radians))
print(string.format("sin(45°) = %.4f", math.sin(radians)))
print(string.format("cos(45°) = %.4f", math.cos(radians)))
print(string.format("tan(45°) = %.4f", math.tan(radians)))
-- Inverse trigonometric functions
local value = 0.5
print(string.format("\nInverse trig functions for %.1f:", value))
print(string.format("arcsin(%.1f) = %.2f° ", value, math.deg(math.asin(value))))
print(string.format("arccos(%.1f) = %.2f°", value, math.deg(math.acos(value))))
print(string.format("arctan(%.1f) = %.2f°", value, math.deg(math.atan(value))))
-- Two-argument arctangent
local y, x = 1, 1
print(string.format("atan2(%d, %d) = %.2f°", y, x, math.deg(math.atan(y, x))))
-- Practical example: distance between two points
local function distance(x1, y1, x2, y2)
return math.sqrt((x2 - x1)^2 + (y2 - y1)^2)
end
-- Angle between two points
local function angle(x1, y1, x2, y2)
return math.deg(math.atan(y2 - y1, x2 - x1))
end
local point1 = {x = 0, y = 0}
local point2 = {x = 3, y = 4}
print(string.format("\nDistance between (%.0f,%.0f) and (%.0f,%.0f): %.2f",
point1.x, point1.y, point2.x, point2.y,
distance(point1.x, point1.y, point2.x, point2.y)))
print(string.format("Angle: %.2f°",
angle(point1.x, point1.y, point2.x, point2.y)))
Expected Output:
Trigonometric functions:
45° = 0.7854 radians
sin(45°) = 0.7071
cos(45°) = 0.7071
tan(45°) = 1.0000
Inverse trig functions for 0.5:
arcsin(0.5) = 30.00°
arccos(0.5) = 60.00°
arctan(0.5) = 26.57°
atan2(1, 1) = 45.00°
Distance between (0,0) and (3,4): 5.00
Angle: 53.13°
Random Numbers
Generate random numbers for games, simulations, and testing:
-- Set random seed for reproducible results
math.randomseed(42)
print("Random number generation:")
print("Random float [0,1):", math.random())
print("Random integer [1,10]:", math.random(10))
print("Random integer [5,15]:", math.random(5, 15))
-- Generate multiple random numbers
print("\n10 random numbers [1,6] (dice rolls):")
for i = 1, 10 do
io.write(math.random(6) .. " ")
end
print()
-- Random number utilities
local random_utils = {}
-- Random float in range [min, max)
function random_utils.random_float(min, max)
return min + (max - min) * math.random()
end
-- Random element from table
function random_utils.random_choice(table)
return table[math.random(#table)]
end
-- Shuffle array (Fisher-Yates algorithm)
function random_utils.shuffle(array)
local n = #array
for i = n, 2, -1 do
local j = math.random(i)
array[i], array[j] = array[j], array[i]
end
return array
end
-- Generate random boolean with probability
function random_utils.random_bool(probability)
probability = probability or 0.5
return math.random() < probability
end
-- Test the utilities
print("\nRandom utilities:")
print("Random float [2.5, 7.5):", random_utils.random_float(2.5, 7.5))
local colors = {"red", "green", "blue", "yellow", "purple"}
print("Random color:", random_utils.random_choice(colors))
local deck = {1, 2, 3, 4, 5}
print("Original deck:", table.concat(deck, ", "))
random_utils.shuffle(deck)
print("Shuffled deck:", table.concat(deck, ", "))
print("Random bool (70% true):", random_utils.random_bool(0.7))
print("Random bool (30% true):", random_utils.random_bool(0.3))
Expected Output:
Random number generation:
Random float [0,1): 0.639586188235
Random integer [1,10]: 9
Random integer [5,15]: 12
10 random numbers [1,6] (dice rolls):
4 1 6 2 5 3 6 1 4 2
Random utilities:
Random float [2.5, 7.5): 5.8479309411764
Random color: blue
Original deck: 1, 2, 3, 4, 5
Shuffled deck: 2, 5, 1, 4, 3
Random bool (70% true): true
Random bool (30% true): false
Number Formatting and Display
Format numbers for display and output:
-- Number formatting with string.format
local pi = math.pi
local large_number = 1234567.89
local small_number = 0.000123
print("Number formatting:")
print("Pi (default):", pi)
print("Pi (2 decimals):", string.format("%.2f", pi))
print("Pi (6 decimals):", string.format("%.6f", pi))
print("Pi (scientific):", string.format("%.2e", pi))
print("Large number:", large_number)
print("With commas:", string.format("%,.2f", large_number)) -- Note: Lua doesn't support comma separator directly
print("Small number:", small_number)
print("Scientific notation:", string.format("%.3e", small_number))
-- Custom number formatting function
local function format_number(num, decimals)
decimals = decimals or 2
local formatted = string.format("%." .. decimals .. "f", num)
-- Add comma separators (simple implementation)
local int_part, dec_part = formatted:match("([^.]+)(.?.*)")
int_part = int_part:reverse():gsub("(%d%d%d)", "%1,"):reverse()
if int_part:sub(1, 1) == "," then
int_part = int_part:sub(2)
end
return int_part .. dec_part
end
print("\nCustom formatting:")
print("1234567.89:", format_number(1234567.89))
print("1000000:", format_number(1000000, 0))
print("123.456:", format_number(123.456, 3))
-- Percentage formatting
local function format_percentage(value, decimals)
decimals = decimals or 1
return string.format("%." .. decimals .. "f%%", value * 100)
end
local ratios = {0.75, 0.1234, 0.05}
print("\nPercentage formatting:")
for _, ratio in ipairs(ratios) do
print(ratio .. " as percentage:", format_percentage(ratio))
end
-- Currency formatting
local function format_currency(amount, symbol)
symbol = symbol or "$"
return symbol .. format_number(amount, 2)
end
local prices = {99.99, 1500, 0.50}
print("\nCurrency formatting:")
for _, price in ipairs(prices) do
print(price .. " as currency:", format_currency(price))
end
Expected Output:
Number formatting:
Pi (default): 3.1415926535898
Pi (2 decimals): 3.14
Pi (6 decimals): 3.141593
Pi (scientific): 3.14e+00
Large number: 1234567.89
With commas: 1234567.89
Small number: 0.000123
Scientific notation: 1.230e-04
Custom formatting:
1234567.89: 1,234,567.89
1000000: 1,000,000
123.456: 123.456
Percentage formatting:
0.75 as percentage: 75.0%
0.1234 as percentage: 12.3%
0.05 as percentage: 5.0%
Currency formatting:
99.99 as currency: $99.99
1500 as currency: $1,500.00
0.5 as currency: $0.50
Practical Number Applications
Statistical Functions
-- Statistical calculation functions
local stats = {}
function stats.mean(numbers)
if #numbers == 0 then return 0 end
local sum = 0
for _, num in ipairs(numbers) do
sum = sum + num
end
return sum / #numbers
end
function stats.median(numbers)
if #numbers == 0 then return 0 end
local sorted = {}
for _, num in ipairs(numbers) do
table.insert(sorted, num)
end
table.sort(sorted)
local n = #sorted
if n % 2 == 1 then
return sorted[(n + 1) / 2]
else
return (sorted[n / 2] + sorted[n / 2 + 1]) / 2
end
end
function stats.standard_deviation(numbers)
if #numbers <= 1 then return 0 end
local mean = stats.mean(numbers)
local sum_sq_diff = 0
for _, num in ipairs(numbers) do
sum_sq_diff = sum_sq_diff + (num - mean)^2
end
return math.sqrt(sum_sq_diff / (#numbers - 1))
end
function stats.min_max(numbers)
if #numbers == 0 then return nil, nil end
local min, max = numbers[1], numbers[1]
for _, num in ipairs(numbers) do
if num < min then min = num end
if num > max then max = num end
end
return min, max
end
-- Test with sample data
local test_scores = {85, 92, 78, 96, 88, 91, 79, 87, 93, 82}
print("Statistical analysis of test scores:")
print("Scores:", table.concat(test_scores, ", "))
print("Mean:", string.format("%.2f", stats.mean(test_scores)))
print("Median:", stats.median(test_scores))
print("Standard deviation:", string.format("%.2f", stats.standard_deviation(test_scores)))
local min_score, max_score = stats.min_max(test_scores)
print("Range:", min_score .. " - " .. max_score)
print("Spread:", max_score - min_score)
Financial Calculations
-- Financial calculation functions
local finance = {}
-- Simple interest: A = P(1 + rt)
function finance.simple_interest(principal, rate, time)
return principal * (1 + rate * time)
end
-- Compound interest: A = P(1 + r/n)^(nt)
function finance.compound_interest(principal, rate, time, compounds_per_year)
compounds_per_year = compounds_per_year or 1
return principal * (1 + rate / compounds_per_year) ^ (compounds_per_year * time)
end
-- Monthly payment for loan: PMT = P * [r(1+r)^n] / [(1+r)^n - 1]
function finance.loan_payment(principal, annual_rate, years)
local monthly_rate = annual_rate / 12
local num_payments = years * 12
if monthly_rate == 0 then
return principal / num_payments
end
return principal * (monthly_rate * (1 + monthly_rate)^num_payments) /
((1 + monthly_rate)^num_payments - 1)
end
-- Break-even point
function finance.break_even(fixed_costs, price_per_unit, variable_cost_per_unit)
return fixed_costs / (price_per_unit - variable_cost_per_unit)
end
-- Example calculations
print("\nFinancial calculations:")
-- Investment comparison
local principal = 10000
local rate = 0.06 -- 6%
local time = 5
local simple = finance.simple_interest(principal, rate, time)
local compound = finance.compound_interest(principal, rate, time, 12) -- Monthly compounding
print(string.format("Investment of $%.2f at 6%% for 5 years:", principal))
print(string.format("Simple interest: $%.2f", simple))
print(string.format("Compound interest (monthly): $%.2f", compound))
print(string.format("Difference: $%.2f", compound - simple))
-- Loan payment calculation
local loan_amount = 250000
local loan_rate = 0.045 -- 4.5%
local loan_years = 30
local monthly_payment = finance.loan_payment(loan_amount, loan_rate, loan_years)
local total_paid = monthly_payment * loan_years * 12
local total_interest = total_paid - loan_amount
print(string.format("\nMortgage calculation ($%.0f at %.1f%% for %d years):",
loan_amount, loan_rate * 100, loan_years))
print(string.format("Monthly payment: $%.2f", monthly_payment))
print(string.format("Total paid: $%.2f", total_paid))
print(string.format("Total interest: $%.2f", total_interest))
Expected Output:
Statistical analysis of test scores:
Scores: 85, 92, 78, 96, 88, 91, 79, 87, 93, 82
Mean: 87.10
Median: 87.5
Standard deviation: 5.95
Range: 78 - 96
Spread: 18
Financial calculations:
Investment of $10000.00 at 6% for 5 years:
Simple interest: $13000.00
Compound interest (monthly): $13488.50
Difference: $488.50
Mortgage calculation ($250000 at 4.5% for 30 years):
Monthly payment: $1266.71
Total paid: $456015.35
Total interest: $206015.35
Common Pitfalls
- Floating-point precision: Be aware of floating-point arithmetic limitations
- Division by zero: Always check for zero divisors
- Integer overflow: Be mindful of integer limits in calculations
- Rounding errors: Use appropriate rounding for financial calculations
- Angle units: Math functions use radians, not degrees
Checks for Understanding
- What's the difference between
/
and//
operators? - How do you convert degrees to radians?
- What does
math.random(5, 10)
return? - How do you format a number to 3 decimal places?
- What's the result of
math.floor(-3.7)
?
Show answers
/
performs regular division (returns float),//
performs floor division (returns integer)- Use
math.rad(degrees)
or multiply bymath.pi / 180
- A random integer between 5 and 10 (inclusive)
- Use
string.format("%.3f", number)
-4
(floor rounds down toward negative infinity)
Next Steps
Now that you understand number operations and the math library, you're ready to explore control flow statements and learn how to use conditions to make decisions in your programs.