Building Custom Number Systems: JavaScript Implementation Guide
What You'll Learn
In this guide, you'll build a complete custom number system from scratch using JavaScript. By the end, you'll have:
- ✅ A working number system with any base (2-36)
- ✅ Conversion between your system and decimal
- ✅ Basic arithmetic operations (add, subtract, multiply, divide)
- ✅ Input validation and error handling
- ✅ A practical understanding of how all number systems work internally
Why Build Your Own Number System?
Creating a number system from scratch helps you understand:
- How computers actually process different bases
- Why certain operations are faster in specific bases
- The algorithms behind built-in conversion functions
- How to design efficient data structures for numerical operations
- The mathematical principles underlying all positional notation systems
Let's start building!
Step 1: Basic Number System Class
class CustomNumberSystem {
constructor(base) {
// Validate base
if (!Number.isInteger(base) || base < 2 || base > 36) {
throw new Error("Base must be an integer between 2 and 36");
}
this.base = base;
this.digits = this.generateDigits(base);
this.digitMap = this.createDigitMap();
}
/**
* Generate digit symbols for the base
* Uses 0-9 for values 0-9, then A-Z for values 10-35
*/
generateDigits(base) {
const digits = [];
// Add numeric digits 0-9
for (let i = 0; i < Math.min(10, base); i++) {
digits.push(i.toString());
}
// Add letter digits A-Z for bases > 10
for (let i = 10; i < base; i++) {
digits.push(String.fromCharCode(65 + i - 10)); // A=65 in ASCII
}
return digits;
}
/**
* Create mapping from digit symbol to numeric value
*/
createDigitMap() {
const map = new Map();
this.digits.forEach((digit, index) => {
map.set(digit.toUpperCase(), index);
map.set(digit.toLowerCase(), index);
});
return map;
}
/**
* Validate if a string contains only valid digits for this base
*/
isValidNumber(str) {
if (typeof str !== "string" || str.length === 0) {
return false;
}
// Remove optional negative sign
const cleanStr = str.startsWith("-") ? str.slice(1) : str;
// Check if all characters are valid digits
return cleanStr.split("").every((char) => this.digitMap.has(char));
}
/**
* Get the maximum digit value for this base
*/
getMaxDigit() {
return this.base - 1;
}
/**
* Display information about this number system
*/
getInfo() {
return {
base: this.base,
digits: this.digits,
maxDigit: this.getMaxDigit(),
example: this.fromDecimal(this.base + 5), // Show example conversion
};
}
}
// Example usage
const binary = new CustomNumberSystem(2);
console.log("Binary system:", binary.getInfo());
// Output: { base: 2, digits: ['0', '1'], maxDigit: 1, example: '111' }
const hex = new CustomNumberSystem(16);
console.log("Hex system:", hex.getInfo());
// Output: { base: 16, digits: ['0','1',...,'9','A',...,'F'], maxDigit: 15, example: 'L' }
Step 2: Decimal to Custom Base Conversion
class CustomNumberSystem {
// ... previous code ...
/**
* Convert decimal number to this number system
* Uses repeated division method
*/
fromDecimal(decimalNumber) {
// Handle edge cases
if (decimalNumber === 0) return "0";
const isNegative = decimalNumber < 0;
let num = Math.abs(decimalNumber);
const result = [];
// Repeated division algorithm
while (num > 0) {
const remainder = num % this.base;
result.unshift(this.digits[remainder]); // Add to front
num = Math.floor(num / this.base);
}
return (isNegative ? "-" : "") + result.join("");
}
/**
* Convert with step-by-step explanation
*/
fromDecimalWithSteps(decimalNumber) {
const steps = [];
const isNegative = decimalNumber < 0;
let num = Math.abs(decimalNumber);
const result = [];
if (num === 0) {
return {
result: "0",
steps: ["0 in any base is 0"],
};
}
// Record each division step
while (num > 0) {
const quotient = Math.floor(num / this.base);
const remainder = num % this.base;
steps.push(
`${num} ÷ ${this.base} = ${quotient} remainder ${remainder} (${this.digits[remainder]})`
);
result.unshift(this.digits[remainder]);
num = quotient;
}
const finalResult = (isNegative ? "-" : "") + result.join("");
steps.push(`Reading remainders from bottom to top: ${finalResult}`);
return {
result: finalResult,
steps: steps,
};
}
}
// Example usage
const octal = new CustomNumberSystem(8);
console.log("Convert 156 to octal:");
const conversion = octal.fromDecimalWithSteps(156);
console.log("Result:", conversion.result); // "234"
console.log("Steps:");
conversion.steps.forEach((step) => console.log(" " + step));
/*
Output:
156 ÷ 8 = 19 remainder 4 (4)
19 ÷ 8 = 2 remainder 3 (3)
2 ÷ 8 = 0 remainder 2 (2)
Reading remainders from bottom to top: 234
*/
Step 3: Custom Base to Decimal Conversion
class CustomNumberSystem {
// ... previous code ...
/**
* Convert number in this system back to decimal
* Uses positional notation: digit × base^position
*/
toDecimal(customNumber) {
if (!this.isValidNumber(customNumber)) {
throw new Error(
`"${customNumber}" is not a valid number in base ${this.base}`
);
}
const isNegative = customNumber.startsWith("-");
const cleanNumber = isNegative ? customNumber.slice(1) : customNumber;
let result = 0;
const digits = cleanNumber.split("").reverse(); // Start from rightmost digit
// Apply positional notation
for (let position = 0; position < digits.length; position++) {
const digitValue = this.digitMap.get(digits[position].toUpperCase());
const positionValue = Math.pow(this.base, position);
result += digitValue * positionValue;
}
return isNegative ? -result : result;
}
/**
* Convert with step-by-step explanation
*/
toDecimalWithSteps(customNumber) {
if (!this.isValidNumber(customNumber)) {
throw new Error(
`"${customNumber}" is not a valid number in base ${this.base}`
);
}
const isNegative = customNumber.startsWith("-");
const cleanNumber = isNegative ? customNumber.slice(1) : customNumber;
const steps = [];
let result = 0;
const digits = cleanNumber.split("").reverse();
steps.push(`Converting ${customNumber} from base ${this.base} to decimal:`);
steps.push(`Using formula: digit × base^position`);
steps.push("");
// Show each position calculation
for (let position = 0; position < digits.length; position++) {
const digit = digits[position].toUpperCase();
const digitValue = this.digitMap.get(digit);
const positionValue = Math.pow(this.base, position);
const contribution = digitValue * positionValue;
steps.push(
`Position ${position}: ${digit} = ${digitValue} × ${this.base}^${position} = ${digitValue} × ${positionValue} = ${contribution}`
);
result += contribution;
}
steps.push("");
steps.push(`Sum: ${result}`);
const finalResult = isNegative ? -result : result;
return {
result: finalResult,
steps: steps,
};
}
}
// Example usage
const hex = new CustomNumberSystem(16);
console.log("Convert A3C from hex to decimal:");
const conversion = hex.toDecimalWithSteps("A3C");
console.log("Result:", conversion.result); // 2620
console.log("Steps:");
conversion.steps.forEach((step) => console.log(step));
/*
Output:
Converting A3C from base 16 to decimal:
Using formula: digit × base^position
Position 0: C = 12 × 16^0 = 12 × 1 = 12
Position 1: 3 = 3 × 16^1 = 3 × 16 = 48
Position 2: A = 10 × 16^2 = 10 × 256 = 2560
Sum: 2620
*/
Step 4: Arithmetic Operations
class CustomNumberSystem {
// ... previous code ...
/**
* Add two numbers in this number system
*/
add(num1, num2) {
// Convert to decimal, add, convert back
const decimal1 = this.toDecimal(num1);
const decimal2 = this.toDecimal(num2);
const sum = decimal1 + decimal2;
return this.fromDecimal(sum);
}
/**
* Subtract two numbers in this number system
*/
subtract(num1, num2) {
const decimal1 = this.toDecimal(num1);
const decimal2 = this.toDecimal(num2);
const difference = decimal1 - decimal2;
return this.fromDecimal(difference);
}
/**
* Multiply two numbers in this number system
*/
multiply(num1, num2) {
const decimal1 = this.toDecimal(num1);
const decimal2 = this.toDecimal(num2);
const product = decimal1 * decimal2;
return this.fromDecimal(product);
}
/**
* Divide two numbers in this number system
*/
divide(num1, num2) {
const decimal1 = this.toDecimal(num1);
const decimal2 = this.toDecimal(num2);
if (decimal2 === 0) {
throw new Error("Division by zero");
}
const quotient = Math.floor(decimal1 / decimal2);
return this.fromDecimal(quotient);
}
/**
* Get remainder (modulo) of division
*/
modulo(num1, num2) {
const decimal1 = this.toDecimal(num1);
const decimal2 = this.toDecimal(num2);
if (decimal2 === 0) {
throw new Error("Modulo by zero");
}
const remainder = decimal1 % decimal2;
return this.fromDecimal(remainder);
}
/**
* Perform arithmetic with detailed steps
*/
calculateWithSteps(num1, operator, num2) {
const steps = [];
steps.push(`Calculating: ${num1} ${operator} ${num2} in base ${this.base}`);
steps.push("");
// Convert operands to decimal
const decimal1 = this.toDecimal(num1);
const decimal2 = this.toDecimal(num2);
steps.push(`Step 1: Convert to decimal`);
steps.push(` ${num1} (base ${this.base}) = ${decimal1} (decimal)`);
steps.push(` ${num2} (base ${this.base}) = ${decimal2} (decimal)`);
steps.push("");
// Perform operation in decimal
let decimalResult;
switch (operator) {
case "+":
decimalResult = decimal1 + decimal2;
break;
case "-":
decimalResult = decimal1 - decimal2;
break;
case "*":
decimalResult = decimal1 * decimal2;
break;
case "/":
if (decimal2 === 0) throw new Error("Division by zero");
decimalResult = Math.floor(decimal1 / decimal2);
break;
case "%":
if (decimal2 === 0) throw new Error("Modulo by zero");
decimalResult = decimal1 % decimal2;
break;
default:
throw new Error(`Unsupported operator: ${operator}`);
}
steps.push(`Step 2: Perform operation in decimal`);
steps.push(` ${decimal1} ${operator} ${decimal2} = ${decimalResult}`);
steps.push("");
// Convert result back
const finalResult = this.fromDecimal(decimalResult);
steps.push(`Step 3: Convert result back to base ${this.base}`);
steps.push(
` ${decimalResult} (decimal) = ${finalResult} (base ${this.base})`
);
return {
result: finalResult,
steps: steps,
};
}
}
// Example usage
const binary = new CustomNumberSystem(2);
console.log("Binary arithmetic:");
const calculation = binary.calculateWithSteps("1011", "+", "1101");
console.log("Result:", calculation.result); // "11000"
console.log("Steps:");
calculation.steps.forEach((step) => console.log(step));
/*
Output:
Calculating: 1011 + 1101 in base 2
Step 1: Convert to decimal
1011 (base 2) = 11 (decimal)
1101 (base 2) = 13 (decimal)
Step 2: Perform operation in decimal
11 + 13 = 24
Step 3: Convert result back to base 2
24 (decimal) = 11000 (base 2)
*/
Step 5: Advanced Features
class CustomNumberSystem {
// ... previous code ...
/**
* Convert between two different custom number systems
*/
static convert(number, fromBase, toBase) {
const fromSystem = new CustomNumberSystem(fromBase);
const toSystem = new CustomNumberSystem(toBase);
// Convert through decimal as intermediate
const decimal = fromSystem.toDecimal(number);
return toSystem.fromDecimal(decimal);
}
/**
* Generate multiplication table for this base
*/
generateMultiplicationTable(size = 10) {
const table = [];
for (let i = 1; i <= size; i++) {
const row = [];
for (let j = 1; j <= size; j++) {
const product = this.multiply(this.fromDecimal(i), this.fromDecimal(j));
row.push(product);
}
table.push(row);
}
return table;
}
/**
* Find all numbers in this base that equal a decimal value
*/
findEquivalents(decimalValue, maxDigits = 6) {
const target = this.fromDecimal(decimalValue);
const equivalents = [target];
// Add leading zeros (all represent same value)
for (let zeros = 1; zeros <= maxDigits - target.length; zeros++) {
const padded = "0".repeat(zeros) + target;
equivalents.push(padded);
}
return equivalents;
}
/**
* Compare two numbers in this base
*/
compare(num1, num2) {
const decimal1 = this.toDecimal(num1);
const decimal2 = this.toDecimal(num2);
if (decimal1 < decimal2) return -1;
if (decimal1 > decimal2) return 1;
return 0;
}
/**
* Sort array of numbers in this base
*/
sort(numbers, ascending = true) {
return numbers.slice().sort((a, b) => {
const comparison = this.compare(a, b);
return ascending ? comparison : -comparison;
});
}
/**
* Get next/previous number in sequence
*/
next(number) {
const decimal = this.toDecimal(number);
return this.fromDecimal(decimal + 1);
}
previous(number) {
const decimal = this.toDecimal(number);
return this.fromDecimal(decimal - 1);
}
/**
* Generate number sequence
*/
sequence(start, count) {
const startDecimal = this.toDecimal(start);
const sequence = [];
for (let i = 0; i < count; i++) {
sequence.push(this.fromDecimal(startDecimal + i));
}
return sequence;
}
/**
* Validate and format number string
*/
format(number, options = {}) {
const { uppercase = true, padStart = 0, prefix = false } = options;
if (!this.isValidNumber(number)) {
throw new Error(`Invalid number: ${number}`);
}
let formatted = number;
// Apply case formatting
if (uppercase) {
formatted = formatted.toUpperCase();
} else {
formatted = formatted.toLowerCase();
}
// Apply padding
if (padStart > 0) {
const isNegative = formatted.startsWith("-");
const cleanNumber = isNegative ? formatted.slice(1) : formatted;
const padded = cleanNumber.padStart(padStart, "0");
formatted = (isNegative ? "-" : "") + padded;
}
// Add base prefix
if (prefix) {
const basePrefix = this.getBasePrefix();
formatted = basePrefix + formatted;
}
return formatted;
}
/**
* Get standard prefix for this base
*/
getBasePrefix() {
switch (this.base) {
case 2:
return "0b";
case 8:
return "0o";
case 16:
return "0x";
default:
return `(${this.base})`;
}
}
}
// Example usage of advanced features
const hex = new CustomNumberSystem(16);
console.log("Convert between bases:");
console.log(CustomNumberSystem.convert("FF", 16, 2)); // "11111111"
console.log(CustomNumberSystem.convert("377", 8, 16)); // "FF"
console.log("\nHex sequence starting from A:");
console.log(hex.sequence("A", 8)); // ["A", "B", "C", "D", "E", "F", "10", "11"]
console.log("\nFormatted numbers:");
console.log(hex.format("abc", { uppercase: true, padStart: 4, prefix: true })); // "0x0ABC"
console.log("\nSorted hex numbers:");
const hexNumbers = ["FF", "1A", "B", "100", "5"];
console.log(hex.sort(hexNumbers)); // ["5", "B", "1A", "FF", "100"]
Step 6: Interactive Number System Calculator
class NumberSystemCalculator {
constructor() {
this.systems = new Map();
this.history = [];
}
/**
* Register a number system
*/
addSystem(name, base) {
this.systems.set(name, new CustomNumberSystem(base));
return this;
}
/**
* Perform calculation in specified base
*/
calculate(systemName, num1, operator, num2) {
if (!this.systems.has(systemName)) {
throw new Error(`System "${systemName}" not found`);
}
const system = this.systems.get(systemName);
const result = system.calculateWithSteps(num1, operator, num2);
// Add to history
this.history.push({
timestamp: new Date(),
system: systemName,
base: system.base,
expression: `${num1} ${operator} ${num2}`,
result: result.result,
steps: result.steps,
});
return result;
}
/**
* Convert between registered systems
*/
convertBetween(number, fromSystem, toSystem) {
if (!this.systems.has(fromSystem) || !this.systems.has(toSystem)) {
throw new Error("One or both systems not found");
}
const from = this.systems.get(fromSystem);
const to = this.systems.get(toSystem);
const decimal = from.toDecimal(number);
const result = to.fromDecimal(decimal);
const conversion = {
original: number,
fromBase: from.base,
toBase: to.base,
result: result,
decimal: decimal,
};
this.history.push({
timestamp: new Date(),
type: "conversion",
...conversion,
});
return conversion;
}
/**
* Get calculation history
*/
getHistory(limit = 10) {
return this.history.slice(-limit);
}
/**
* Clear history
*/
clearHistory() {
this.history = [];
}
/**
* List all registered systems
*/
listSystems() {
const systems = [];
for (const [name, system] of this.systems) {
systems.push({
name: name,
base: system.base,
digits: system.digits.join(""),
maxDigit: system.getMaxDigit(),
});
}
return systems;
}
}
// Create calculator with common number systems
const calculator = new NumberSystemCalculator()
.addSystem("binary", 2)
.addSystem("octal", 8)
.addSystem("decimal", 10)
.addSystem("hex", 16)
.addSystem("base12", 12) // Dozenal system
.addSystem("base20", 20); // Vigesimal system
// Example usage
console.log("Available systems:");
console.table(calculator.listSystems());
console.log("\nCalculation in binary:");
const binaryCalc = calculator.calculate("binary", "1011", "+", "110");
console.log("Result:", binaryCalc.result);
console.log("\nConversion between systems:");
const conversion = calculator.convertBetween("FF", "hex", "binary");
console.log(
`${conversion.original} (base ${conversion.fromBase}) = ${conversion.result} (base ${conversion.toBase})`
);
console.log("\nRecent history:");
console.table(calculator.getHistory(5));
Step 7: Practical Applications
Custom Base for Data Compression
/**
* Base64-like encoding using custom base
*/
class CustomEncoder {
constructor(base) {
if (base > 36) {
throw new Error("This example supports bases up to 36");
}
this.system = new CustomNumberSystem(base);
}
/**
* Encode string to custom base representation
*/
encode(text) {
// Convert text to numbers (ASCII values)
const numbers = [];
for (let i = 0; i < text.length; i++) {
numbers.push(text.charCodeAt(i));
}
// Convert each ASCII value to custom base
const encoded = numbers.map((num) => this.system.fromDecimal(num));
return encoded.join("-"); // Use delimiter
}
/**
* Decode from custom base back to string
*/
decode(encoded) {
// Split by delimiter and convert back to ASCII
const parts = encoded.split("-");
const ascii = parts.map((part) => this.system.toDecimal(part));
// Convert ASCII values back to characters
return String.fromCharCode(...ascii);
}
}
// Example usage
const base36Encoder = new CustomEncoder(36);
const message = "Hello";
const encoded = base36Encoder.encode(message);
const decoded = base36Encoder.decode(encoded);
console.log(`Original: ${message}`);
console.log(`Encoded (base 36): ${encoded}`);
console.log(`Decoded: ${decoded}`);
// Output:
// Original: Hello
// Encoded (base 36): 28-2T-2W-2W-2X
// Decoded: Hello
Roman Numeral System (Non-Positional)
/**
* Roman numeral system (different from positional systems)
*/
class RomanNumeralSystem {
constructor() {
this.values = [
{ symbol: "M", value: 1000 },
{ symbol: "CM", value: 900 },
{ symbol: "D", value: 500 },
{ symbol: "CD", value: 400 },
{ symbol: "C", value: 100 },
{ symbol: "XC", value: 90 },
{ symbol: "L", value: 50 },
{ symbol: "XL", value: 40 },
{ symbol: "X", value: 10 },
{ symbol: "IX", value: 9 },
{ symbol: "V", value: 5 },
{ symbol: "IV", value: 4 },
{ symbol: "I", value: 1 },
];
}
toRoman(num) {
if (num <= 0 || num >= 4000) {
throw new Error("Roman numerals only support 1-3999");
}
let result = "";
let remaining = num;
for (const { symbol, value } of this.values) {
while (remaining >= value) {
result += symbol;
remaining -= value;
}
}
return result;
}
fromRoman(roman) {
let result = 0;
let i = 0;
for (const { symbol, value } of this.values) {
while (roman.substr(i, symbol.length) === symbol) {
result += value;
i += symbol.length;
}
}
return result;
}
}
// Compare positional vs non-positional systems
const decimal = new CustomNumberSystem(10);
const roman = new RomanNumeralSystem();
const number = 1984;
console.log(`Decimal: ${number}`);
console.log(`Roman: ${roman.toRoman(number)}`); // MCMLXXXIV
console.log(
`Binary: ${decimal.fromDecimal(number)} -> ${CustomNumberSystem.convert(
decimal.fromDecimal(number),
10,
2
)}`
);
Step 8: Performance Optimization
/**
* Optimized number system with caching
*/
class OptimizedNumberSystem extends CustomNumberSystem {
constructor(base) {
super(base);
this.conversionCache = new Map();
this.maxCacheSize = 1000;
}
/**
* Cached decimal conversion
*/
toDecimal(customNumber) {
const cacheKey = `${customNumber}_to_decimal`;
if (this.conversionCache.has(cacheKey)) {
return this.conversionCache.get(cacheKey);
}
const result = super.toDecimal(customNumber);
this.addToCache(cacheKey, result);
return result;
}
/**
* Cached custom base conversion
*/
fromDecimal(decimalNumber) {
const cacheKey = `${decimalNumber}_from_decimal`;
if (this.conversionCache.has(cacheKey)) {
return this.conversionCache.get(cacheKey);
}
const result = super.fromDecimal(decimalNumber);
this.addToCache(cacheKey, result);
return result;
}
/**
* Smart cache management
*/
addToCache(key, value) {
if (this.conversionCache.size >= this.maxCacheSize) {
// Remove oldest entry
const firstKey = this.conversionCache.keys().next().value;
this.conversionCache.delete(firstKey);
}
this.conversionCache.set(key, value);
}
/**
* Get cache statistics
*/
getCacheStats() {
return {
size: this.conversionCache.size,
maxSize: this.maxCacheSize,
hitRatio: this.cacheHits / (this.cacheHits + this.cacheMisses) || 0,
};
}
/**
* Clear cache
*/
clearCache() {
this.conversionCache.clear();
}
}
// Performance comparison
console.time("Standard system");
const standard = new CustomNumberSystem(16);
for (let i = 0; i < 1000; i++) {
standard.fromDecimal(i);
standard.toDecimal(standard.fromDecimal(i));
}
console.timeEnd("Standard system");
console.time("Optimized system");
const optimized = new OptimizedNumberSystem(16);
for (let i = 0; i < 1000; i++) {
optimized.fromDecimal(i);
optimized.toDecimal(optimized.fromDecimal(i));
}
console.timeEnd("Optimized system");
console.log("Cache stats:", optimized.getCacheStats());
Complete Working Example
Here's a complete, runnable example that demonstrates all concepts:
<!DOCTYPE html>
<html>
<head>
<title>Custom Number System Calculator</title>
<style>
body {
font-family: monospace;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.result {
background: #f0f0f0;
padding: 10px;
margin: 10px 0;
}
.steps {
background: #e8f4f8;
padding: 10px;
margin: 10px 0;
}
input,
select,
button {
margin: 5px;
padding: 5px;
}
</style>
</head>
<body>
<h1>Custom Number System Calculator</h1>
<div>
<h3>Conversion</h3>
<input
type="text"
id="inputNumber"
placeholder="Enter number"
value="255"
/>
<select id="fromBase">
<option value="2">Binary</option>
<option value="8">Octal</option>
<option value="10" selected>Decimal</option>
<option value="16">Hex</option>
<option value="12">Base 12</option>
</select>
to
<select id="toBase">
<option value="2">Binary</option>
<option value="8">Octal</option>
<option value="10">Decimal</option>
<option value="16" selected>Hex</option>
<option value="12">Base 12</option>
</select>
<button onclick="convert()">Convert</button>
</div>
<div>
<h3>Arithmetic</h3>
<input type="text" id="num1" placeholder="First number" value="FF" />
<select id="operator">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">×</option>
<option value="/">/</option>
<option value="%">%</option>
</select>
<input type="text" id="num2" placeholder="Second number" value="1A" />
in base
<select id="calcBase">
<option value="2">Binary</option>
<option value="8">Octal</option>
<option value="10">Decimal</option>
<option value="16" selected>Hex</option>
</select>
<button onclick="calculate()">Calculate</button>
</div>
<div id="result" class="result" style="display:none;"></div>
<div id="steps" class="steps" style="display:none;"></div>
<script>
// Include all the CustomNumberSystem code here
// ... (previous code) ...
function convert() {
const number = document.getElementById("inputNumber").value;
const fromBase = parseInt(document.getElementById("fromBase").value);
const toBase = parseInt(document.getElementById("toBase").value);
try {
const result = CustomNumberSystem.convert(number, fromBase, toBase);
document.getElementById(
"result"
).innerHTML = ``;
document.getElementById("result").style.display = "block";
// Show detailed steps
const fromSystem = new CustomNumberSystem(fromBase);
const toSystem = new CustomNumberSystem(toBase);
const decimal = fromSystem.toDecimal(number);
const steps = [
`Step 1: Convert ${number} from base ${fromBase} to decimal: ${decimal}`,
`Step 2: Convert ${decimal} from decimal to base ${toBase}: ${result}`,
];
document.getElementById("steps").innerHTML =
"<strong>Steps:</strong><br>" + steps.join("<br>");
document.getElementById("steps").style.display = "block";
} catch (error) {
document.getElementById(
"result"
).innerHTML = ``;
document.getElementById("result").style.display = "block";
document.getElementById("steps").style.display = "none";
}
}
function calculate() {
const num1 = document.getElementById("num1").value;
const operator = document.getElementById("operator").value;
const num2 = document.getElementById("num2").value;
const base = parseInt(document.getElementById("calcBase").value);
try {
const system = new CustomNumberSystem(base);
const calculation = system.calculateWithSteps(num1, operator, num2);
document.getElementById(
"result"
).innerHTML = ``;
document.getElementById("result").style.display = "block";
document.getElementById("steps").innerHTML =
"<strong>Steps:</strong><br>" + calculation.steps.join("<br>");
document.getElementById("steps").style.display = "block";
} catch (error) {
document.getElementById(
"result"
).innerHTML = ``;
document.getElementById("result").style.display = "block";
document.getElementById("steps").style.display = "none";
}
}
// Initialize with example calculation
convert();
</script>
</body>
</html>
Key Takeaways
✅ Number systems follow consistent patterns - understanding one helps you build any other
✅ Conversion algorithms are universal - repeated division and positional notation work for all bases
✅ Caching improves performance - conversion is expensive, so store results
✅ Validation is crucial - always check input before processing
✅ Step-by-step explanations help users understand the process
✅ Arithmetic can be done in any base by converting through decimal
✅ Real-world applications exist for custom bases (encoding, compression, specialized domains)
By building your own number system, you've gained deep insight into:
- How JavaScript's built-in
parseInt()
andtoString()
work internally - Why certain bases are preferred for specific applications
- The mathematical principles underlying all positional notation
- How to design efficient algorithms for numerical operations
- The importance of error handling and input validation
This knowledge will help you debug number-related issues, optimize performance-critical code, and understand how computers handle different numeric representations at a fundamental level.