Memory Unit Converter: From 1024 to Petabytes - Implementation Deep Dive
Memory Unit Converter: From 1024 to Petabytes - Implementation Deep Dive#
Building a file upload feature recently, I needed to display file sizes. The backend returns byte counts in the millions - users stare at 15728640 and wonder, “How big is that?” So I built a memory unit converter.
The Core Question: Why 1024?#
Computers store data in bits, with 8 bits forming 1 Byte. After that, each unit scales by 1024 (that’s 2^10):
1 KB = 1024 Bytes
1 MB = 1024 KB = 1,048,576 Bytes
1 GB = 1024 MB = 1,073,741,824 Bytes
1 TB = 1024 GB = 1,099,511,627,776 Bytes
1 PB = 1024 TB = 1,125,899,906,842,624 Bytes
Why 1024 instead of 1000?
Computers use binary. 1024 = 2^10 is a binary “round number.” Hard drive manufacturers use decimal (1 KB = 1000 Bytes), which is why your “1TB” drive shows as 931 GB on your computer - not a defect, just different standards.
Conversion Algorithm#
The Core Concept#
Converting between units is simple: convert to bytes first, then to the target unit:
interface Unit {
value: string // 'B', 'KB', 'MB'...
label: string // 'Bytes (B)', 'Kilobytes (KB)'...
factor: number // 1024^n
}
const units: Unit[] = [
{ value: "B", label: "Bytes (B)", factor: 1 },
{ value: "KB", label: "Kilobytes (KB)", factor: 1024 },
{ value: "MB", label: "Megabytes (MB)", factor: 1024 ** 2 },
{ value: "GB", label: "Gigabytes (GB)", factor: 1024 ** 3 },
{ value: "TB", label: "Terabytes (TB)", factor: 1024 ** 4 },
{ value: "PB", label: "Petabytes (PB)", factor: 1024 ** 5 },
]
function convert(value: number, from: string, to: string): number {
const fromUnit = units.find(u => u.value === from)
const toUnit = units.find(u => u.value === to)
if (!fromUnit || !toUnit) return 0
const bytes = value * fromUnit.factor // To bytes first
return bytes / toUnit.factor // To target unit
}
// Example: 2 GB to MB
convert(2, 'GB', 'MB') // 2048
Auto-selecting the Best Unit#
When users input 15728640 bytes, we want to display 15 MB automatically:
function formatBytes(bytes: number, decimals: number = 2): string {
if (bytes === 0) return '0 Bytes'
const k = 1024
const dm = decimals < 0 ? 0 : decimals
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB']
// Calculate the appropriate unit index
const i = Math.floor(Math.log(bytes) / Math.log(k))
// Convert and format
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
}
formatBytes(15728640) // "15 MB"
formatBytes(1073741824) // "1 GB"
formatBytes(1234567890) // "1.15 GB"
formatBytes(1023) // "1023 Bytes"
Algorithm explained:
Math.log(bytes) / Math.log(1024) computes the logarithm base 1024. The result’s integer part indicates how many unit boundaries we’ve crossed:
Math.log(15728640) / Math.log(1024) ≈ 2.91→ index 2, which is MBMath.log(1073741824) / Math.log(1024) = 3→ index 3, which is GB
Calculating All Units at Once#
To show conversions to all units simultaneously:
function calculateAllConversions(value: number, from: string) {
const fromUnit = units.find(u => u.value === from)
if (!fromUnit) return []
const bytes = value * fromUnit.factor
return units.map(u => ({
...u,
result: bytes / u.factor
}))
}
// Example: 1 GB in all units
calculateAllConversions(1, 'GB')
/*
[
{ value: 'B', result: 1073741824 },
{ value: 'KB', result: 1048576 },
{ value: 'MB', result: 1024 },
{ value: 'GB', result: 1 },
{ value: 'TB', result: 0.0009765625 },
{ value: 'PB', result: 9.5367431640625e-7 }
]
*/
Cache results with useMemo to avoid recalculating:
const result = useMemo(() => {
const numValue = parseFloat(value) || 0
return convert(numValue, fromUnit, toUnit)
}, [value, fromUnit, toUnit])
const allConversions = useMemo(() => {
const numValue = parseFloat(value) || 0
return calculateAllConversions(numValue, fromUnit)
}, [value, fromUnit])
Number Formatting & Precision#
Large Number Display#
1073741824 isn’t readable. Use toLocaleString() for thousands separators:
// Default formatting
result.toLocaleString() // "1,073,741,824"
// Control decimal places
result.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 4
})
// "1,073,741,824.0000" or "1.1525" (auto-adjusted)
Floating Point Precision Pitfalls#
JavaScript’s floating-point math has precision issues:
0.1 + 0.2 // 0.30000000000000004
1024 * 0.0009765625 // 0.9999999999999999 (should be 1)
Solution: use toFixed() or toPrecision():
function formatResult(value: number, maxDecimals: number = 6): string {
// Round first, then remove trailing zeros
const fixed = value.toFixed(maxDecimals)
return parseFloat(fixed).toString()
}
formatResult(0.9999999999999999) // "1"
formatResult(15.123456789) // "15.123457"
Practical Applications#
1. File Upload Size Limits#
function checkFileSize(file: File, maxSizeMB: number): boolean {
const maxSizeBytes = maxSizeMB * 1024 * 1024
return file.size <= maxSizeBytes
}
// Check if file exceeds 10MB
checkFileSize(file, 10)
2. Storage Space Statistics#
interface StorageInfo {
total: number
used: number
available: number
}
function formatStorageInfo(info: StorageInfo) {
return {
total: formatBytes(info.total),
used: formatBytes(info.used),
available: formatBytes(info.available),
usagePercent: ((info.used / info.total) * 100).toFixed(1) + '%'
}
}
formatStorageInfo({
total: 107374182400, // 100 GB
used: 75161927680, // 70 GB
available: 32212254720 // 30 GB
})
// { total: "100 GB", used: "70 GB", available: "30 GB", usagePercent: "70.0%" }
3. Bandwidth Calculations#
Network bandwidth is typically in Mbps (megabits per second), but download speeds display as MB/s:
function bandwidthToSpeed(mbps: number): string {
const bytesPerSecond = (mbps * 1000000) / 8 // Mbps → MB/s
return formatBytes(bytesPerSecond) + '/s'
}
bandwidthToSpeed(100) // "12.5 MB/s" (100Mbps actual download speed)
bandwidthToSpeed(1000) // "125 MB/s" (1Gbps)
Edge Cases#
1. Very Large Values#
JavaScript’s Number.MAX_SAFE_INTEGER is 2^53 - 1 (about 9 PB). Larger integers may lose precision:
function safeConvert(value: number, from: string, to: string): number {
const fromUnit = units.find(u => u.value === from)
const toUnit = units.find(u => u.value === to)
if (!fromUnit || !toUnit) return 0
// Use BigInt for very large values
if (value > Number.MAX_SAFE_INTEGER) {
const bytes = BigInt(value) * BigInt(fromUnit.factor)
return Number(bytes / BigInt(toUnit.factor))
}
return (value * fromUnit.factor) / toUnit.factor
}
2. Negative Numbers and Zero#
function formatBytes(bytes: number): string {
if (bytes === 0) return '0 Bytes'
if (bytes < 0) return '-' + formatBytes(-bytes)
// Normal conversion logic...
}
3. Non-numeric Input#
function parseInput(value: string): number {
const parsed = parseFloat(value)
return isNaN(parsed) ? 0 : parsed
}
The Result#
Based on these principles, I built: Memory Unit Converter
Features:
- B / KB / MB / GB / TB / PB conversions
- Real-time calculation for all units
- Decimal and negative number support
- Thousands separator formatting
The implementation isn’t complex, but involves many details: base selection, precision control, formatting, and edge cases. Hope this helps!
Related tools: Unit Converter | Timestamp Converter