JavaScript電卓作りで全部エラー!私が3日かけて学んだ計算処理の罠
「電卓くらい簡単に作れるでしょ」
そう思っていた時期が私にもありました。
こんにちは、とまだです。
初めてJavaScriptで電卓を作ろうとした時、私は完全に甘く見ていました。
結果?
3日間エラーと格闘する羽目になったんです。
- 数字ボタンを押しても画面に何も出ない
- 計算ボタンを押したら「NaN」の文字が...
- 「0.1 + 0.2」を計算したら「0.30000000000000004」
「え?なんで?」の連続でした。
でも今なら分かります。電卓作りは、JavaScriptの基本が全部詰まった最高の教材だったんです。
今回は現役のエンジニア、そして元プログラミングスクール講師としての経験から、JavaScript電卓の作り方について解説します。
なぜ電卓作りでみんな失敗するのか
簡単に言うと、電卓作りには見た目以上の難しさが潜んでいるからです。
私が最初に作った電卓、こんな感じでした:
// 私の黒歴史コード
function calculate() {
let num1 = document.getElementById("num1").value;
let num2 = document.getElementById("num2").value;
let result = num1 + num2;
alert(result); // 「12」じゃなくて「1」「2」って表示される...
}
「1 + 2 = 12」
...は?
これ、文字列連結になってたんです。JavaScriptあるあるですよね。
電卓作りの3大ハマりポイント
1. 文字列と数値の罠
// ❌ よくある失敗
"1" + "2" // "12" (文字列の連結)
// ✅ 正しい方法
Number("1") + Number("2") // 3
// または
parseFloat("1") + parseFloat("2") // 3
私はこれに気づくまで1時間悩みました...。
2. 小数点の恐怖
// JavaScriptの有名なバグ?いいえ、仕様です
0.1 + 0.2 // 0.30000000000000004
// 対策:小数点以下を丸める
Math.round((0.1 + 0.2) * 10) / 10 // 0.3
お客さんに「100円 + 200円 = 300.00000000000006円です」なんて表示したら、信用失いますよね。
3. ゼロ除算の悪夢
// ❌ エラーチェックなし
function divide(a, b) {
return a / b; // b が 0 だと Infinity
}
// ✅ エラーチェックあり
function divide(a, b) {
if (b === 0) {
return "エラー:0で割ることはできません";
}
return a / b;
}
実際に動く!シンプル電卓を作ってみよう
では、私の失敗を踏まえた「動く電卓」を作ってみましょう。
Step1: HTML(見た目)
<!DOCTYPE html>
<html>
<head>
<title>JavaScript電卓</title>
<style>
.calculator {
width: 300px;
margin: 50px auto;
padding: 20px;
background: #f0f0f0;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.display {
background: white;
padding: 10px;
margin-bottom: 10px;
text-align: right;
font-size: 24px;
border-radius: 5px;
min-height: 40px;
}
.buttons {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}
button {
padding: 20px;
font-size: 18px;
border: none;
border-radius: 5px;
cursor: pointer;
background: white;
}
button:hover {
background: #e0e0e0;
}
.operator {
background: #ff9800;
color: white;
}
.operator:hover {
background: #f57c00;
}
.equals {
background: #4caf50;
color: white;
grid-column: span 2;
}
.clear {
background: #f44336;
color: white;
}
</style>
</head>
<body>
<div class="calculator">
<div class="display" id="display">0</div>
<div class="buttons">
<button class="clear" onclick="clearDisplay()">C</button>
<button onclick="appendNumber('(')">(</button>
<button onclick="appendNumber(')')">)</button>
<button class="operator" onclick="appendOperator('/')">÷</button>
<button onclick="appendNumber('7')">7</button>
<button onclick="appendNumber('8')">8</button>
<button onclick="appendNumber('9')">9</button>
<button class="operator" onclick="appendOperator('*')">×</button>
<button onclick="appendNumber('4')">4</button>
<button onclick="appendNumber('5')">5</button>
<button onclick="appendNumber('6')">6</button>
<button class="operator" onclick="appendOperator('-')">−</button>
<button onclick="appendNumber('1')">1</button>
<button onclick="appendNumber('2')">2</button>
<button onclick="appendNumber('3')">3</button>
<button class="operator" onclick="appendOperator('+')">+</button>
<button onclick="appendNumber('0')">0</button>
<button onclick="appendNumber('.')">.</button>
<button class="equals" onclick="calculate()">=</button>
</div>
</div>
<script src="calculator.js"></script>
</body>
</html>
Step2: JavaScript(動き)
// calculator.js
let display = document.getElementById('display');
let currentInput = '0';
let shouldResetDisplay = false;
// 画面をクリア
function clearDisplay() {
currentInput = '0';
display.textContent = '0';
}
// 数字を追加
function appendNumber(num) {
// 新しい計算を始める場合
if (shouldResetDisplay) {
currentInput = '';
shouldResetDisplay = false;
}
// 最初の0を消す
if (currentInput === '0' && num !== '.') {
currentInput = '';
}
// 小数点の重複チェック
if (num === '.' && currentInput.includes('.')) {
return;
}
currentInput += num;
display.textContent = currentInput;
}
// 演算子を追加
function appendOperator(op) {
// 連続した演算子を防ぐ
const lastChar = currentInput[currentInput.length - 1];
if (['+', '-', '*', '/'].includes(lastChar)) {
currentInput = currentInput.slice(0, -1);
}
currentInput += op;
display.textContent = currentInput;
shouldResetDisplay = false;
}
// 計算実行
function calculate() {
try {
// evalは危険だけど、電卓なら大丈夫
// 本番環境では使わないでね!
let result = eval(currentInput);
// 小数点の誤差対策
if (typeof result === 'number') {
result = Math.round(result * 100000000) / 100000000;
}
display.textContent = result;
currentInput = result.toString();
shouldResetDisplay = true;
} catch (error) {
display.textContent = 'エラー';
currentInput = '0';
shouldResetDisplay = true;
}
}
実践的な電卓の活用例
1. ECサイトの料金計算
// 商品価格 × 個数 × 税率
function calculateTotal(price, quantity, taxRate = 1.1) {
const subtotal = price * quantity;
const total = subtotal * taxRate;
// 円単位で丸める
return Math.floor(total);
}
// 使用例
const itemPrice = 980;
const quantity = 3;
const total = calculateTotal(itemPrice, quantity);
console.log(`合計: ${total}円`); // 合計: 3234円
2. ローン計算機
// 月々の返済額を計算
function calculateMonthlyPayment(principal, annualRate, years) {
const monthlyRate = annualRate / 100 / 12;
const numberOfPayments = years * 12;
if (monthlyRate === 0) {
return principal / numberOfPayments;
}
const payment = principal *
(monthlyRate * Math.pow(1 + monthlyRate, numberOfPayments)) /
(Math.pow(1 + monthlyRate, numberOfPayments) - 1);
return Math.round(payment);
}
// 1000万円を年利2%で35年ローン
const monthly = calculateMonthlyPayment(10000000, 2, 35);
console.log(`月々の返済額: ${monthly.toLocaleString()}円`);
3. 割り勘計算機
function splitBill(total, people, includesTax = true) {
let amount = total;
// 税込みでない場合は税を加算
if (!includesTax) {
amount = amount * 1.1;
}
// 一人当たりの金額(10円単位で切り上げ)
const perPerson = Math.ceil(amount / people / 10) * 10;
return {
perPerson: perPerson,
total: perPerson * people,
extra: perPerson * people - amount
};
}
// 5人で12,345円を割り勘
const result = splitBill(12345, 5);
console.log(`一人${result.perPerson}円(${result.extra}円のお釣り)`);
プロが教える実装のコツ
1. 状態管理をシンプルに
// 電卓の状態を管理するオブジェクト
const calculator = {
displayValue: '0',
firstOperand: null,
waitingForOperand: false,
operator: null
};
// 状態をリセット
function reset() {
calculator.displayValue = '0';
calculator.firstOperand = null;
calculator.waitingForOperand = false;
calculator.operator = null;
}
2. 演算を関数化
const operations = {
'+': (a, b) => a + b,
'-': (a, b) => a - b,
'*': (a, b) => a * b,
'/': (a, b) => b !== 0 ? a / b : 'Error',
'=': (a, b) => b
};
function performOperation(operator) {
const inputValue = parseFloat(calculator.displayValue);
if (calculator.firstOperand === null) {
calculator.firstOperand = inputValue;
} else if (calculator.operator) {
const result = operations[calculator.operator](
calculator.firstOperand,
inputValue
);
calculator.displayValue = String(result);
calculator.firstOperand = result;
}
calculator.waitingForOperand = true;
calculator.operator = operator;
}
3. キーボード対応
document.addEventListener('keydown', (event) => {
const key = event.key;
if (key >= '0' && key <= '9') {
appendNumber(key);
} else if (key === '.') {
appendNumber('.');
} else if (key === '+' || key === '-' || key === '*' || key === '/') {
appendOperator(key);
} else if (key === 'Enter' || key === '=') {
calculate();
} else if (key === 'Escape' || key === 'c' || key === 'C') {
clearDisplay();
} else if (key === 'Backspace') {
deleteLastChar();
}
});
よくある質問と解決策
Q: なぜ0.1 + 0.2が0.3にならないの?
A: これはJavaScriptの仕様です。解決策:
// 方法1: 小数点以下の桁数を指定
function roundToDecimal(num, decimals) {
return Math.round(num * Math.pow(10, decimals)) / Math.pow(10, decimals);
}
// 方法2: toFixedを使う(文字列になるので注意)
(0.1 + 0.2).toFixed(2) // "0.30"
// 方法3: 整数にしてから計算
(10 + 20) / 100 // 0.3
Q: evalは危険って聞いたけど?
A: その通りです!本番環境では使わないでください。代替案:
// 安全な計算機の実装例
function safeCalculate(expression) {
// 許可する文字だけを通す
const sanitized = expression.replace(/[^0-9+\-*/().\s]/g, '');
// トークンに分解して解析
const tokens = sanitized.match(/\d+\.?\d*|[+\-*/()]/g);
// ここで構文解析して計算
// (実装は複雑なので省略)
}
Q: もっと高機能な電卓を作りたい
A: 段階的に機能を追加しましょう:
- メモリ機能(M+, M-, MR, MC)
- 関数電卓(sin, cos, log, √)
- 履歴機能(計算履歴の保存)
- グラフ表示(Chart.jsなどを使用)
まとめ
JavaScript電卓作りは、最初は「簡単そう」に見えて、実は奥が深いプロジェクトです。
私も3日間悩みましたが、その分学んだことも多かったです:
- 文字列と数値の変換は必須
- 小数点の誤差に注意
- エラー処理を忘れずに
- 状態管理をシンプルに
電卓作りで学んだことは、他のプロジェクトでも必ず役立ちます。
フォームの入力値計算、ECサイトの料金表示、データの集計処理...どこでも使える知識です。
最初は動かなくても大丈夫。私だって「1 + 2 = 12」から始まったんですから。
大切なのは、エラーを恐れずに手を動かすこと。
さあ、あなたも電卓作りに挑戦してみませんか?
きっと「なるほど!」の連続が待っていますよ。
著者について

とまだ
フルスタックエンジニア
Learning Next の創設者。Ruby on Rails と React を中心に、プログラミング教育に情熱を注いでいます。初心者が楽しく学べる環境作りを目指しています。
著者の詳細を見る →