Fraction Operations Calculator
Add, subtract, multiply, or divide any fractions — including mixed numbers — with the full step-by-step working, a visual bar model, and the answer in lowest terms.
Compute
Type each number as a/b (like 3/4), a mixed number (like 1 1/2), or a whole number.
Why the steps work
Adding / subtracting: the denominator names the piece size, so both fractions must use the same size before the tops can combine. The LCD renames without changing values.
Multiplying: (a/b) × (c/d) = ac/bd — numerators multiply because you take a fraction of a fraction of the pieces; denominators multiply because the pieces subdivide.
Dividing: keep-change-flip turns “how many fit?” into a multiplication, which always gives the same answer.
"use strict";
/* == PURE (tested by test_tool.js) == */ function tgcd(a, b) { a = Math.abs(a); b = Math.abs(b); while (b) { var t = a % b; a = b; b = t; } return a || 1; } function tred(n, d) { if (d === 0) return { ok: false, err: 'zero denominator' }; if (d < 0) { n = -n; d = -d; } if (n === 0) return { ok: true, n: 0, d: 1 }; var g = tgcd(n, d); return { ok: true, n: n / g, d: d / g }; } function tlcm(a, b) { return Math.abs(a * b) / tgcd(a, b); } /* parse "a/b" | "w a/b" | integer | decimal -> improper {n,d} | {ok:false, err} */ function tparseFrac(raw) { var s = String(raw).replace(/−/g, '-').trim(); if (!s) return { ok: false, err: 'empty input' }; var sign = 1; if (s[0] === '-') { sign = -1; s = s.slice(1).trim(); } else if (s[0] === '+') { s = s.slice(1).trim(); } var m, n, d; function scaleDec(x) { var i = x.indexOf('.'); if (i < 0) return { n: parseInt(x, 10), d: 1 }; var pl = x.length - i - 1, k = Math.pow(10, pl); return { n: Math.round(parseFloat(x) * k), d: k }; } if ((m = /^(\d+)\s+(\d+)\s*\/\s*(\d+)$/.exec(s))) { d = parseInt(m[3], 10); if (d === 0) return { ok: false, err: 'denominator zero' }; n = parseInt(m[1], 10) * d + parseInt(m[2], 10); return tred(sign * n, d); } var t = s.replace(/\s+/g, ''); if ((m = /^(\d+(?:\.\d+)?)\/(\d+(?:\.\d+)?)$/.exec(t))) { var an = scaleDec(m[1]), ad = scaleDec(m[2]); if (ad.n === 0) return { ok: false, err: 'denominator zero' }; return tred(sign * an.n * ad.d, ad.n * an.d); } if ((m = /^(\d*\.?\d+)$/.exec(t))) { var a = scaleDec(m[1]); return tred(sign * a.n, a.d); } return { ok: false, err: 'could not parse "' + raw + '"' }; } function tadd(a, b) { return tred(a.n * b.d + b.n * a.d, a.d * b.d); } function tsub(a, b) { return tred(a.n * b.d - b.n * a.d, a.d * b.d); } function tmul(a, b) { return tred(a.n * b.n, a.d * b.d); } function tdiv(a, b) { if (b.n === 0) return { ok: false, err: 'division by zero' }; return tred(a.n * b.d, a.d * b.n); } /* opCalc: full step data for a op b. op in 'add','sub','mul','div'. */ function opCalc(t1, t2, op) { var A = tparseFrac(t1), B = tparseFrac(t2); if (!A.ok) return { ok: false, err: 'First number: ' + A.err + '. Use a/b, "1 1/2", or an integer.' }; if (!B.ok) return { ok: false, err: 'Second number: ' + B.err + '. Use a/b, "1 1/2", or an integer.' }; var steps = [], R; if (op === 'add' || op === 'sub') { var L = tlcm(A.d, B.d); var a2 = A.n * (L / A.d), b2 = B.n * (L / B.d); var word = op === 'add' ? 'Add' : 'Subtract'; var sym = op === 'add' ? '+' : '−'; steps.push({ t: 'Rename both fractions with the LCD ' + L, d: A.n + '/' + A.d + ' = ' + a2 + '/' + L + '; ' + B.n + '/' + B.d + ' = ' + b2 + '/' + L }); var top = op === 'add' ? a2 + b2 : a2 - b2; steps.push({ t: word + ' the tops, keep the denominator', d: a2 + ' ' + sym + ' ' + b2 + ' = ' + top + ' → ' + top + '/' + L }); R = op === 'add' ? tadd(A, B) : tsub(A, B); steps.push({ t: 'Simplify: divide top and bottom by ' + tgcd(top, L), d: top + '/' + L + ' = ' + R.n + '/' + R.d }); } else if (op === 'mul') { var g1 = tgcd(A.n, B.d), g2 = tgcd(B.n, A.d); if (g1 > 1 || g2 > 1) { steps.push({ t: 'Cancel common factors first', d: (g1 > 1 ? A.n + ' and ' + B.d + ' share ' + g1 + '. ' : '') + (g2 > 1 ? B.n + ' and ' + A.d + ' share ' + g2 + '.' : '') }); } steps.push({ t: 'Multiply straight across', d: '(' + A.n + ' × ' + B.n + ')/(' + A.d + ' × ' + B.d + ') = ' + (A.n * B.n) + '/' + (A.d * B.d) }); R = tmul(A, B); steps.push({ t: 'Simplify to lowest terms', d: R.n + '/' + R.d }); } else if (op === 'div') { if (B.n === 0) return { ok: false, err: 'Cannot divide by zero.' }; steps.push({ t: 'Keep-change-flip', d: A.n + '/' + A.d + ' × ' + B.d + '/' + B.n }); steps.push({ t: 'Multiply straight across', d: '(' + A.n + ' × ' + B.d + ')/(' + A.d + ' × ' + B.n + ') = ' + (A.n * B.d) + '/' + (A.d * B.n) }); R = tdiv(A, B); steps.push({ t: 'Simplify to lowest terms', d: R.n + '/' + R.d }); } else { return { ok: false, err: 'unknown operation' }; } return { ok: true, A: A, B: B, op: op, result: R, steps: steps }; } /* == /PURE == */
(function () { 'use strict'; var OP = 'add'; function el(id) { return document.getElementById(id); } function showErr(id, msg) { var e = el(id); if (msg) { e.textContent = msg; e.classList.add('show'); } else { e.classList.remove('show'); } } function fhtml(n, d) { if (d === 1) return String(n); return (n < 0 ? '−' : '') + '' + Math.abs(n) + '' + d + ''; } function mixedLine(n, d) { if (d === 1) return String(n); var neg = n < 0, a = Math.abs(n); if (a > d) { var w = Math.floor(a / d), r = a % d; if (r === 0) return (neg ? '-' : '') + w; return (neg ? '-' : '') + w + ' ' + r + '/' + d; } return (neg ? '-' : '') + a + '/' + d; } function barSVG(n, d, fill) { var W = 220, H = 44, x0 = 6, bw = W - 12, neg = n < 0, an = Math.abs(n); var whole = Math.floor(an / d), rem = an % d, s = ''; s += ''; return s; } el('fb').innerHTML = 'Enter two fractions (a/b, "1 1/2", or whole numbers), pick an operation, and see every step.';
var opBtns = document.querySelectorAll('.opbtn'); opBtns.forEach(function (btn) { btn.addEventListener('click', function () { opBtns.forEach(function (b) { b.classList.remove('active'); }); btn.classList.add('active'); OP = btn.dataset.op; }); });
el('calc').addEventListener('click', function () { showErr('err', ''); var r = opCalc(el('in1').value, el('in2').value, OP); if (!r.ok) { showErr('err', r.err); el('res').classList.remove('show'); return; } var opSym = { add: '+', sub: '−', mul: '×', div: '÷' }[r.op]; el('ans').innerHTML = fhtml(r.A.n, r.A.d) + ' ' + opSym + ' ' + fhtml(r.B.n, r.B.d) + ' = ' + fhtml(r.result.n, r.result.d) + '' + ' (' + mixedLine(r.result.n, r.result.d) + ')'; el('steps').innerHTML = r.steps.map(function (st, i) { return '
' + st.d + '
'; }).join(''); el('viz').innerHTML = '
' + '
' + '
' + '
' + '
'; el('res').classList.add('show'); }); })();