Skip to content

Instantly share code, notes, and snippets.

@tociyuki tociyuki/dentaku.html
Last active Aug 29, 2015

Embed
What would you like to do?
Decimal Fraction calculator
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>電卓</title>
<style>
#stage {
width: 106pt;
font-size: 14pt;
text-align: right;
background-color: #eeeeee;
}
#operator {
font-size: 9pt;
text-align: right;
color: #888888;
}
.calcclear { font-size: 8pt; width: 32px }
.calcinput { font-size: 12pt; width: 32px }
</style>
<script src="dentaku.js"></script>
</head>
<body>
<form id="calc">
<table>
<tr>
<th colspan="4" id="stage">0.</th>
</tr>
<tr>
<td><input id="clear" class="calcclear" type="button" value="C" /></td>
<td colspan="2" id="operator">&nbsp;</td>
<td><input id="reset" class="calcclear" type="button" value="AC" /></td>
</tr>
<tr>
<td><input id="digit7" class="calcinput" type="button" value="" /></td>
<td><input id="digit8" class="calcinput" type="button" value="" /></td>
<td><input id="digit9" class="calcinput" type="button" value="" /></td>
<td><input id="divide" class="calcinput" type="button" value="÷" /></td>
</tr>
<tr>
<td><input id="digit4" class="calcinput" type="button" value="" /></td>
<td><input id="digit5" class="calcinput" type="button" value="" /></td>
<td><input id="digit6" class="calcinput" type="button" value="" /></td>
<td><input id="times" class="calcinput" type="button" value="×" /></td>
</tr>
<tr>
<td><input id="digit1" class="calcinput" type="button" value="" /></td>
<td><input id="digit2" class="calcinput" type="button" value="" /></td>
<td><input id="digit3" class="calcinput" type="button" value="" /></td>
<td><input id="minus" class="calcinput" type="button" value="" /></td>
</tr>
<tr>
<td><input id="digit0" class="calcinput" type="button" value="" /></td>
<td><input id="point" class="calcinput" type="button" value="" /></td>
<td><input id="enter" class="calcinput" type="button" value="" /></td>
<td><input id="plus" class="calcinput" type="button" value="" /></td>
</tr>
</table>
</form>
</body>
</html>
(function(){
var __INFO__ = {
'name': 'dentaku.js',
'description': 'Dentaku (pocket calculator) application.',
'version': '0.02',
'author': 'MIZUTANI Tociyuki',
'license': 'GNU General Public License Version 2',
'see also': 'D. Knuth, "The Art of Computer Programing VOLUME 2 Third Edition" (1998)',
};
var Decimal = {};
Decimal.ORD0 = '0'.charCodeAt(0);
Decimal.addu = function(u, v) {
var i, j, a, c, w;
w = ''
j = u.length;
i = v.length;
c = 0;
while (i > 0) {
a = u.charCodeAt(--j) - Decimal.ORD0 + v.charCodeAt(--i) - Decimal.ORD0 + c;
c = a >= 10 ? 1 : 0;
if (c == 1)
a -= 10;
w = String.fromCharCode(a + Decimal.ORD0) + w;
}
while (j > 0) {
a = u.charCodeAt(--j) - Decimal.ORD0 + c;
c = a >= 10 ? 1 : 0;
if (c == 1)
a -= 10;
w = String.fromCharCode(a + Decimal.ORD0) + w;
}
if (c != 0)
w = String.fromCharCode(c + Decimal.ORD0) + w;
return w;
};
Decimal.subu = function(u, v) {
var i, j, a, c, w;
w = ''
j = u.length;
i = v.length;
c = 1;
while (i > 0) {
a = u.charCodeAt(--j) - v.charCodeAt(--i) + c + 9;
c = a >= 10 ? 1 : 0;
if (c == 1)
a -= 10;
w = String.fromCharCode(a + Decimal.ORD0) + w;
}
while (j > 0) {
a = u.charCodeAt(--j) - Decimal.ORD0 + c + 9;
c = a >= 10 ? 1 : 0;
if (c == 1)
a -= 10;
w = String.fromCharCode(a + Decimal.ORD0) + w;
}
return w;
};
Decimal.mulu = function(uu, vv) {
var m, i, j, a, c, x, u=[], w=[], ww;
m = uu.length;
j = vv.length;
for (i = 0; i < m; ++i)
u[i] = uu.charCodeAt(i) - Decimal.ORD0;
for (i = 0; i < m + j; ++i)
w[i] = 0;
while (--j >= 0) {
x = vv.charCodeAt(j) - Decimal.ORD0;
if (x > 0) {
i = m;
c = 0;
while (--i >= 0) {
a = u[i] * x + w[i + j + 1] + c;
c = Math.floor(a / 10);
w[i + j + 1] = a - c * 10;
}
w[j] = c;
}
}
ww = '';
for (i = 0, m = w.length; i < m; ++i)
ww = ww + String.fromCharCode(w[i] + Decimal.ORD0);
return ww;
};
Decimal.divmodus = function(u, v) {
var q, r, i, n, a;
q = '';
r = 0;
for (i = 0, n = u.length; i < n; ++i) {
r = r * 10 + u.charCodeAt(i) - Decimal.ORD0;
a = Math.floor(r / v);
r -= a * v;
q = q + String.fromCharCode(a + Decimal.ORD0);
}
return [q, '' + r];
};
Decimal.divmodu = function(uu, vv) {
var d, i, j, c, c1, c2, m, n, qhat, u01, u2, a, a1, a2, c, u=[0], v=[0], q='';
m = uu.length - vv.length;
n = vv.length;
for (i = 0; i < n + m; ++i)
u[i + 1] = uu.charCodeAt(i) - Decimal.ORD0;
for (i = 0; i < n; ++i)
v[i] = vv.charCodeAt(i) - Decimal.ORD0;
d = Math.floor(10 / (vv.charCodeAt(0) - Decimal.ORD0 + 1));
if (d > 1) {
i = m + n + 1;
c = 0;
while (--i >= 0) {
a = u[i] * d + c;
c = Math.floor(a / 10);
u[i] = a - c * 10;
}
i = n;
c = 0;
while (--i >= 0) {
a = v[i] * d + c;
c = Math.floor(a / 10);
v[i] = a - c * 10;
}
}
for (j = 0; j <= m; ++j) {
u01 = u[j] * 10 + u[j + 1];
u2 = j + 2 >= u.length ? 0 : u[j + 2];
qhat = Math.floor(u01 / v[0]);
if (qhat > 9)
qhat = 9;
while (qhat * v[1] > (u01 - qhat * v[0]) * 10 + u2)
--qhat;
if (qhat > 0) {
c1 = 0;
c2 = 1;
i = n;
while (--i >= 0) {
a1 = qhat * v[i] + c1;
c1 = Math.floor(a1 / 10);
a1 -= c1 * 10;
a2 = u[j + i + 1] - a1 + c2 + 9;
c2 = a2 >= 10 ? 1 : 0;
u[j + i + 1] = c2 == 1 ? (a2 - 10) : a2;
}
if (u[j] - c1 + c2 < 1) {
--qhat;
c = 0;
i = n;
while (--i >= 0) {
a = u[j + i + 1] + v[i] + c;
c = a >= 10 ? 1 : 0;
u[j + i + 1] = c > 0 ? (a - 10) : a;
}
}
}
u[j] = 0;
q = q + String.fromCharCode(qhat + Decimal.ORD0);
}
uu = '';
if (d <= 1) {
for (i = m + 1; i <= m + n; ++i)
uu = uu + String.fromCharCode(u[i] + Decimal.ORD0);
}
else {
c = 0;
for (i = 0; i < n; ++i) {
a = c * 10 + u[m + i + 1];
a1 = Math.floor(a / d);
c = a - a1 * d;
uu = uu + String.fromCharCode(a1 + Decimal.ORD0);
}
}
return [q, uu];
};
Decimal.add = function(u, v) {
var su='', sv='', un, vn;
u = '' + u; v = '' + v;
if (u.charAt(0) == '-') {
su = '-';
u = u.slice(1);
}
if (v.charAt(0) == '-') {
sv = '-';
v = v.slice(1);
}
un = u.length;
vn = v.length;
if (((su == '-') && (sv == '')) || ((su == '') && (sv == '-'))) {
if (un > vn || ((un == vn) && (('' + u) >= v))) {
u = Decimal.subu(u, v);
}
else {
su = sv;
u = Decimal.subu(v, u);
}
}
else {
if (un > vn || ((un == vn) && (('' + u) >= v)))
u = Decimal.addu(u, v);
else
u = Decimal.addu(v, u);
}
u = u.replace(/^0+/, '');
if (u == '')
u = '0';
else if (su == '-')
u = su + u;
return u;
};
Decimal.abs = function(u) {
if (u.charAt(0) == '-')
u = u.slice(1);
return u;
};
Decimal.neg = function(u) {
if (u.charAt(0) == '-')
u = u.slice(1);
else if (u != '0')
u = '-' + u;
return u;
};
Decimal.sub = function(u, v) { return Decimal.add(u, Decimal.neg(v)); };
Decimal.mul = function(u, v) {
var su='', sv='';
u = '' + u; v = '' + v;
if ((u == '0') || (v == '0'))
return '0';
if (u == '1')
return v;
if (u == '-1')
return Decimal.neg(v);
if (v == '1')
return u;
if (v == '-1')
return Decimal.neg(u);
if (u.charAt(0) == '-') {
su = '-';
u = u.slice(1);
}
if (v.charAt(0) == '-') {
sv = '-';
v = v.slice(1);
}
u = Decimal.mulu(u, v);
u = u.replace(/^0+/, '');
if (u == '')
u = '0';
else if ((su == '-') ^ (sv == '-'))
u = '-' + u;
return u;
};
Decimal.cmp = function(u, v) {
var su='', sv='', nu, nv;
u = '' + u; v = '' + v;
if (u.charAt(0) == '-') {
su = '-';
}
if (v.charAt(0) == '-') {
sv = '-';
}
if ((su == '-') && (sv == ''))
return -1;
if ((su == '') && (sv == '-'))
return +1;
if ((su == '') && (sv == '')) {
nu = u.length; nv = v.length;
return nu < nv ? -1 : nu > nv ? +1 : u < v ? -1 : u == v ? 0 : +1;
}
else {
u = u.slice(1); v = v.slice(1);
nu = u.length; nv = v.length;
return nu < nv ? +1 : nu > nv ? -1 : u < v ? +1 : u == v ? 0 : -1;
}
};
Decimal.divmod = function(u, v) {
var su='', sv='', sq = '', sr = '', qr, cp;
if (v == '0')
throw 'DIV/0';
if (u == '0')
return ['0', '0'];
if (v == '1')
return [u, '0'];
if (v == '-1')
return [Decimal.neg(u), '0'];
u = '' + u; v = '' + v;
if (u.charAt(0) == '-') {
su = '-';
u = u.slice(1);
}
if (v.charAt(0) == '-') {
sv = '-';
v = v.slice(1);
}
if (v.length == 1) {
qr = Decimal.divmodus(u, v);
}
else {
cp = Decimal.cmp(u, v);
qr = cp < 0 ? ['0', u] : cp == 0 ? ['1', '0'] : Decimal.divmodu(u, v);
}
if ((su == '-') && (sv == '-')) {
sr = '-';
}
else if ((su == '') && (sv == '-')) {
sq = '-'; sr = '-';
qr[0] = Decimal.addu(qr[0], '1');
qr[1] = Decimal.subu(v, qr[1]);
}
else if ((su == '-') && (sv == '')) {
sq = '-';
qr[0] = Decimal.addu(qr[0], '1');
qr[1] = Decimal.subu(v, qr[1]);
}
qr[0] = qr[0].replace(/^0+/, '');
if (qr[0] == '')
qr[0] = '0';
else if (sq == '-')
qr[0] = '-' + qr[0];
qr[1] = qr[1].replace(/^0+/, '');
if (qr[1] == '')
qr[1] = '0';
else if (sr == '-')
qr[1] = '-' + qr[1];
return qr;
};
Decimal.gcd = function(u, v) {
var t;
u = Decimal.abs(u); v = Decimal.abs(v);
if (u == '0')
return v;
if (v == '0')
return u;
if (Decimal.cmp(u, v) < 0) {
t = u; u = v; v = t;
}
while (v != '0') {
t = Decimal.divmod(u, v)[1];
u = v;
v = t;
}
return u;
};
function Fraction(n, d) {
var sn='', sd='', s, f;
if (n.charAt(0) == '-') {
sn = '-';
n = n.slice(1);
}
if (d.charAt(0) == '-') {
sd = '-';
d = d.slice(1);
}
if (d == '0')
throw 'NUMERATOR/0';
s = (sn == '-') ^ (sd == '-') ? '-' : '';
f = Decimal.gcd(n, d);
this.numerator = s + Decimal.divmod(n, f)[0];
this.denominator = Decimal.divmod(d, f)[0];
}
Fraction.prototype.add = function(x) {
return (new Fraction(
Decimal.add(Decimal.mul(this.numerator, x.denominator),
Decimal.mul(this.denominator, x.numerator)),
Decimal.mul(this.denominator, x.denominator)));
};
Fraction.prototype.sub = function(x) {
return (new Fraction(
Decimal.sub(Decimal.mul(this.numerator, x.denominator),
Decimal.mul(this.denominator, x.numerator)),
Decimal.mul(this.denominator, x.denominator)));
};
Fraction.prototype.mul = function(x) {
return (new Fraction(
Decimal.mul(this.numerator, x.numerator),
Decimal.mul(this.denominator, x.denominator)));
};
Fraction.prototype.div = function(x) {
return (new Fraction(
Decimal.mul(this.numerator, x.denominator),
Decimal.mul(this.denominator, x.numerator)));
};
Fraction.from_fixed = function(x) {
var m, s, i, f;
if (x == '')
return (new Fraction('0', '1'));
m = /^(-?)(0|[1-9][0-9]*)(?:[.]([0-9]*))?$/.exec(x);
s = m[1];
i = m[2];
f = m[3] !== undefined ? m[3] : '';
i = i + f;
i = i.replace(/^0+/, '');
if (i == '')
return (new Fraction('0', '1'));
return (new Fraction(s + i, '1' + Fraction.zeros(f.length)));
};
Fraction.prototype.to_string = function() {
return this.numerator + '/' + this.denominator;
};
Fraction.prototype.to_fixed = function(prec) {
var a, n, d, en, ed, w, s = '', f, e;
n = this.numerator; d = this.denominator;
if (n.charAt(0) == '-') {
s = '-';
n = n.slice(1);
}
if (n == '0')
return '0';
en = n.length;
ed = d.length;
if (prec + 1 > en)
n = n + Fraction.zeros(prec + 1 - en);
if (prec + 1 > ed)
d = d + Fraction.zeros(prec + 1 - ed);
a = this.normalize_float('0' + n + Fraction.zeros(prec + 1), en, prec + 1, false);
n = a[0], en = a[1];
a = this.normalize_float('0' + d + Fraction.zeros(prec + 1), ed, prec + 1, false);
d = a[0], ed = a[1];
w = Decimal.divmodu(n + Fraction.zeros(prec + 1), d)[0];
w = Fraction.zeros(prec * 2 + 3 - w.length) + w;
a = this.normalize_float(w, prec + 1 + en - ed, prec, true);
f = a[0]; e = a[1];
if (e <= -prec + 1)
return s + '0.' + Fraction.zeros(prec - 1);
if (prec < e && e <= prec + 8) {
f = f.slice(0, e - 8) + '.' + f.slice(e - 8);
return s + f + 'E8';
}
if (e > prec) {
f = f.slice(0, 1) + '.' + f.slice(1);
--e;
return s + f + 'E' + e;
}
if (e <= 0) {
a = this.normalize_float(w, prec + 1 + en - ed, prec, false);
f = a[0]; e = a[1];
f = '0' + Fraction.zeros(-e) + f;
f = f.slice(0, prec + 1);
if (f.charAt(prec) >= '5')
f = Decimal.addu(f, '10');
f = f.slice(0, prec);
f = f.slice(0, 1) + '.' + f.slice(1);
}
else {
f = f.slice(0, e) + '.' + f.slice(e);
}
while (f.charAt(f.length - 1) == '0')
f = f.slice(0, f.length - 1);
if (f.charAt(f.length - 1) == '.')
f = f.slice(0, f.length - 1);
return s + f;
};
Fraction.prototype.normalize_float = function(fg, e, prec, rnd) {
var f, i=0;
while (! /^0+$/.test(fg)) {
while (fg.charAt(0) != '0') {
fg = '0' + fg.slice(0, fg.length - 1);
++e;
}
while (fg.charAt(1) == '0') {
fg = fg.slice(1, fg.length - 1) + '0';
--e;
}
f = fg.slice(0, prec + 1);
if (rnd && fg.charAt(prec + 1) >= '5')
f = Decimal.addu(f, '1');
fg = f + Fraction.zeros(prec);
if (fg.charAt(0) == '0')
break;
}
return [fg.slice(1, prec + 1), e];
};
Fraction.zeros = function(n) {
var i, t;
t = '';
for (i = 0; i < n; ++i)
t += '0';
return t;
};
// (new Calc()).state
// 0: just after calculation or without input
// 1: integer part
// 2: fractional part
// 3: operator part
// 9: error
var Calc = function(){
this.precision = 8;
this.reset();
};
Calc.prototype.calculate = function(op, u, v){
switch (op) {
case '+':
return u.add(v);
case '-':
return u.sub(v);
case '*':
return u.mul(v);
case '/':
return u.div(v);
}
throw 'Illegal Operator ' + op;
};
Calc.prototype.reset = function(){
this.state = 0;
this.register = null; // fraction
this.operator = '';
this.digits = 0;
this.fixed = ''; // fixed
this.value = null; // fraction
this.koperator = '';
this.kvalue = null; // fraction
};
Calc.prototype.clear = function(){
this.state = 0;
this.digits = 0;
this.fixed = '';
this.value = null;
};
Calc.prototype.put_digit = function(d){
if (this.state == 0 || this.state == 3) {
this.clear();
if (d != '0') {
this.concat_digit(d);
this.state = 1;
}
}
else if (this.state == 1 || this.state == 2) {
this.concat_digit(d);
}
};
Calc.prototype.put_point = function(){
if (this.state == 0 || this.state == 3) {
this.clear();
this.concat_digit('0');
this.fixed += '.';
this.state = 2;
}
else if (this.state == 1) {
this.fixed += '.';
this.state = 2;
}
};
Calc.prototype.concat_digit = function(d){
if (this.digits < this.precision) {
this.fixed += d;
++this.digits;
}
};
Calc.prototype.shift_operator = function(op){
if (this.state == 9) {
this.register = null;
this.operator = '';
}
else if (op == '=') {
this.register = this.value;
this.operator = '';
this.state = 0;
}
else {
this.register = this.value;
this.operator = op;
this.koperator = '';
this.kvalue = null;
this.state = 3;
}
};
Calc.prototype.operate = function(op1){
var op, u, v;
if (this.state == 9)
return;
u = this.register;
if (u === null)
u = new Fraction('0', '1');
if (this.value === null)
this.value = Fraction.from_fixed(this.fixed);
v = this.value;
if ((this.state == 3) && (op1 != '=')) {
if (this.operator == op1) { // CASIO style constant calculations
this.koperator = this.operator;
this.kvalue = v;
this.operator = '';
}
else {
this.operator = op1;
}
return;
}
if ((this.operator == '') && (op1 != '=')) {
this.shift_operator(op1);
return;
}
if ((this.operator == '') && (op1 == '=') && (this.koperator != '')) {
op = this.koperator;
u = v;
v = this.kvalue;
}
else { // SHARP style constant calculations
this.koperator = op = this.operator;
this.kvalue = v;
}
try {
this.value = this.calculate(op, u, v);
this.fixed = this.value.to_fixed(this.precision);
this.check_overflow();
this.shift_operator(op1);
}
catch (e) {
this.operator = '';
this.koperator = '';
this.state = 9;
}
};
Calc.prototype.check_overflow = function() {
if (/E8$/.test(this.fixed)) {
this.fixed = this.fixed.slice(0, this.fixed.length - 2);
this.state = 9;
}
else if (/^0[.]0+$/.test(this.fixed)) {
this.state = 9;
}
else if (this.fixed.indexOf('E') >= 0) {
this.state = 9;
}
};
var $ = function(id){ return document.getElementById(id.slice(1)) };
var redraw = function(calc){
var s;
s = calc.fixed;
if (s == '')
s = '0';
if (s.indexOf('.') < 0)
s += '.';
if (calc.state == 9)
s = 'E' + s;
$('#stage').innerHTML = s;
$('#operator').innerHTML = calc.operator == '' ? '&nbsp;' : calc.operator;
};
var setup = function(){
var calc = new Calc();
$('#reset').onclick = function(){ calc.reset(); redraw(calc); };
$('#clear').onclick = function(){ calc.clear(); redraw(calc); };
$('#digit0').onclick = function(){ calc.put_digit('0'); redraw(calc); };
$('#digit1').onclick = function(){ calc.put_digit('1'); redraw(calc); };
$('#digit2').onclick = function(){ calc.put_digit('2'); redraw(calc); };
$('#digit3').onclick = function(){ calc.put_digit('3'); redraw(calc); };
$('#digit4').onclick = function(){ calc.put_digit('4'); redraw(calc); };
$('#digit5').onclick = function(){ calc.put_digit('5'); redraw(calc); };
$('#digit6').onclick = function(){ calc.put_digit('6'); redraw(calc); };
$('#digit7').onclick = function(){ calc.put_digit('7'); redraw(calc); };
$('#digit8').onclick = function(){ calc.put_digit('8'); redraw(calc); };
$('#digit9').onclick = function(){ calc.put_digit('9'); redraw(calc); };
$('#point').onclick = function(){ calc.put_point(); redraw(calc); };
$('#plus').onclick = function(){ calc.operate('+'); redraw(calc); };
$('#minus').onclick = function(){ calc.operate('-'); redraw(calc); };
$('#times').onclick = function(){ calc.operate('*'); redraw(calc); };
$('#divide').onclick = function(){ calc.operate('/'); redraw(calc); };
$('#enter').onclick = function(){ calc.operate('='); redraw(calc); };
calc.reset(); redraw(calc);
};
window.onload = setup;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.