You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

303 lines
15 KiB
HTML

<body style="background-color:#333333; color:#EEEEEE; font-family:arial, inconsolata, monospace, sans-serif; font-size:24px;">
<div>
Division algorithm with repeating decimal detection test<br><br>
<input id="num1" type="text" onchange="divide();" oninput='this.onchange()' onfocus='this.select();' style="width:100px; background-color:#555555; color:#EEEEEE; font-size:20px; text-align:center;" />&nbsp;÷&nbsp;
<input id="num2" type="text" onchange="divide();" oninput='this.onchange()' onfocus='this.select();' style="width:100px; background-color:#555555; color:#EEEEEE; font-size:20px; text-align:center;" />
</div><br>
<table style="font-size:20px;">
<tr>
<td>long_division(A, B):</td>
<td id='long_div_result'></td>
</tr>
<tr>
<td>(A / B):</td>
<td id='standard_result'></td>
</tr>
</table>
<br>
<table>
<tr>
<td>p_max:</td>
<td>
<input type="button" value="&minus;" onclick="document.getElementById('p_max').value = parseInt(document.getElementById('p_max').value) - 1; divide();" >
<input id="p_max" value="4" type="text" onchange="divide();" oninput="this.onchange();" onfocus="this.select();" style="width:40px; background-color:#555555; color:#EEEEEE; font-size:16px; text-align:center;" />
<input type="button" value="&plus;" onclick="document.getElementById('p_max').value = parseInt(document.getElementById('p_max').value) + 1; divide();" >
</td>
<td>(Maximum precision)</td>
</tr>
<tr>
<td>p_min:</td>
<td>
<input type="button" value="&minus;" onclick="document.getElementById('p_min').value = parseInt(document.getElementById('p_min').value) - 1; divide();" >
<input id="p_min" value="0" type="text" onchange="divide();" oninput="this.onchange();" onfocus="this.select();" style="width:40px; background-color:#555555; color:#EEEEEE; font-size:16px; text-align:center;" />
<input type="button" value="&plus;" onclick="document.getElementById('p_min').value = parseInt(document.getElementById('p_min').value) + 1; divide();" >
</td>
<td>(Minimum precision)</td>
</tr>
<tr>
<td>Repeat singles twice:<br>(i.e. 1.<span style="text-decoration:overline;">33</span> instead of 1.<span style="text-decoration:overline;">3</span>)</td>
<td style="text-align:center;"><input id="singles" type="checkbox" onchange="divide();" checked /></td>
</tr>
<tr>
<td>Use ≈ symbol for non-exact output:</td>
<td style="text-align:center;"><input id="approx" type="checkbox" onchange="divide();" checked /></td>
</tr>
</table>
<br>
<table>
<tr><td>Quick Tests:</td></tr>
<tr><td></td></tr>
<tr><td><input type="button" value="4 ÷ 3" style="background-color:#555555; color:#EEEEEE; font-size:16px;" onclick="divide(4, 3);" /> (1-digit repeating pattern)</td></tr>
<tr><td><input type="button" value="16 ÷ 9" style="background-color:#555555; color:#EEEEEE; font-size:16px;" onclick="divide(16, 9);" /> (1-digit repeating pattern)</td></tr>
<tr><td><input type="button" value="64 ÷ 27" style="background-color:#555555; color:#EEEEEE; font-size:16px;" onclick="divide(64, 27);" /> (3-digit repeating pattern)</td></tr>
<tr><td><input type="button" value="30 ÷ 13" style="background-color:#555555; color:#EEEEEE; font-size:16px;" onclick="divide(30, 13);" /> (6-digit repeating pattern)</td></tr>
<tr><td><input type="button" value="43 ÷ 18" style="background-color:#555555; color:#EEEEEE; font-size:16px;" onclick="divide(43, 18);" /> (1 non-repeating + 1-digit repeating pattern)</td></tr>
<tr><td><input type="button" value="256 ÷ 135" style="background-color:#555555; color:#EEEEEE; font-size:16px;" onclick="divide(256, 135);" /> (1 non-repeating + 3-digit repeating pattern)</td></tr>
<tr><td>&nbsp;</td></tr>
<tr><td><input type="button" value="8 ÷ 4" style="background-color:#555555; color:#EEEEEE; font-size:16px;" onclick="divide(8, 4);" /> (integer result)</td></tr>
<tr><td><input type="button" value="8 ÷ 5" style="background-color:#555555; color:#EEEEEE; font-size:16px;" onclick="divide(8, 5);" /> (1-decimal evenly-divisible result)</td></tr>
<tr><td><input type="button" value="1 ÷ 64" style="background-color:#555555; color:#EEEEEE; font-size:16px;" onclick="divide(1, 64);" /> (6-decimal evenly-divisible result)</td></tr>
<tr><td><input type="button" value="&minus;256 ÷ 135" style="background-color:#555555; color:#EEEEEE; font-size:16px;" onclick="divide(-256, 135);" /> (negative A)</td></tr>
<tr><td><input type="button" value="256 ÷ &minus;135" style="background-color:#555555; color:#EEEEEE; font-size:16px;" onclick="divide(256, -135);" /> (negative B)</td></tr>
<tr><td><input type="button" value="&minus;256 ÷ &minus;135" style="background-color:#555555; color:#EEEEEE; font-size:16px;" onclick="divide(-256, -135);" /> (negative A and B)</td></tr>
</table>
</body>
<script>
function divide(A, B) {
if(A) { document.getElementById('num1').value = A; }
if(B) { document.getElementById('num2').value = B; }
console.log('A:', A, 'B:', B);
A = A || parseFloat(document.getElementById('num1').value);
B = B || parseFloat(document.getElementById('num2').value);
var p_max = document.getElementById('p_max').value;
var p_min = document.getElementById('p_min').value;
var singles = document.getElementById('singles').checked;
if (document.getElementById('approx').checked) { var approx = '≈'; } else { var approx = ''; }
var options = {
'p_max': p_max,
'p_min': p_min,
'repeat_singles': singles,
'approx': approx,
//'OL_open': '{{overline|',
//'OL_close': '}}',
}
//var result = long_division_core(A, B);
var result = long_division(A, B, options);
document.getElementById('long_div_result').innerHTML = result;
document.getElementById('standard_result').innerHTML = (A / B);
}
function long_division_core(A, B) {
if (isNaN(A / B)) { return NaN; }
var Max_Depth = 32; // maximum number of decimal places
var Radix_Point = '.';
var Base = 10;
var Sign = '';
if ((A / B) < 0) { Sign = '-'; }
var Dividend = Math.abs(parseFloat(A));
var Divisor = Math.abs(parseFloat(B));
var Quotient = Dividend / Divisor;
var Remainder = Dividend % Divisor;
var Result = Math.floor(Quotient).toString() + Radix_Point; // Use floor division to determine the front part of the number immediately
Dividend = Remainder * Base;
var i = 0;
while (i < Max_Depth) {
Quotient = Dividend / Divisor;
Remainder = Dividend % Divisor;
Dividend = Remainder * 10;
Result += Math.floor(Quotient).toString();
i += 1;
}
return Sign.concat(Result);
}
function long_division(A, B, options) {
console.log('BEGIN:', A, '÷', B)
if (isNaN(A / B) || !isFinite(A / B)) { console.log('Answer is NaN or Infinity. Function aborted.'); return ''; }
var OL_open = '<span style="text-decoration:overline;">'; // Overline markup opening tag
var OL_close = '</span>'; // Overline markup closing tag. Used in conjunction with OL_open to surround the repeating numbers. May be set to control markup, separate it with parentheses, or simply left blank, etc.
var p_max = 8; // Maximum number of decimal places
var p_min = 3; // Minimum number of decimal places
var Approx = '≈'; // Symbol to be used for "approximately equal to". Can be set to '~' or blank if desired, etc.
var Radix_Point = '.'; // Character to use for the radix point ("decimal point")
var Base = 10; // Number base system to use
var Minus_Sign = '&minus;'; // Character to preceed negative numbers
var Plus_Sign = ''; // Character to preceed positive numbers
var RepeatSinglesFlag = true; // Display single-digit repeating patterns as 1.(33) instead of 1.(3)
if (typeof(options) === 'number') { // If 3rd argument is a number rather than a dictionary, use it as the p_max value
p_max = options;
}
else if(options) {
if ('OL_open' in options) { OL_open = options['OL_open']; }
if ('OL_close' in options) { OL_close = options['OL_close']; }
if ('p_max' in options) { p_max = options['p_max']; }
if ('p_min' in options) { p_min = options['p_min']; }
if ('radix' in options) { Radix_Point = options['radix']; }
if ('approx' in options) { Approx = options['approx']; }
if ('minus' in options) { Minus_Sign = options['minus']; }
if ('plus' in options) { Plus_Sign = options['plus']; }
if ('base' in options) { Base = options['base']; }
if ('repeat_singles' in options) { RepeatSinglesFlag = options['repeat_singles']; }
}
p_max = parseInt(p_max);
p_min = parseInt(p_min);
Base = parseInt(Base);
if (p_max < 0 || p_min < 0 || p_max < p_min || isNaN(p_max) || isNaN(p_min) || !isFinite(p_max) || !isFinite(p_min)) {
console.log('Invalid p_max and p_min values. Both values must be non-negative numbers, and p_min cannot be greater than p_max. p_max:', p_max, 'p_min', p_min)
return '';
}
if (isNaN(Base)) {
console.log('Invalid Base value. Must be an integer number. Base:', Base);
return '';
}
if (p_max == 0) {
var Result = Math.round(A / B).toFixed(0);
if (Result != (A / B)) { Result = '≈'.concat(Result); }
return Result;
}
var Max_Depth = 32; // Depth of internal calculations, regardless of p_max or p_min settings
var Decimal_Digits = '';
var Previous_Dividends = {};
var Prefix = '';
var Repetend = '';
var RepeatFlag = false;
var ApproxFlag = true;
var Sign = Plus_Sign;
// Determine if answer will be negative, then use the absolute value of inputs for the rest of the calculations.
if ((A < 0) ? !(B < 0) : (B < 0)) { Sign = Minus_Sign; } // If (A is negative) XOR (B is negative) then final result will be negative.
var Dividend = Math.abs(parseFloat(A));
var Divisor = Math.abs(parseFloat(B));
var Quotient = Dividend / Divisor;
var Remainder = Dividend % Divisor;
var Result = Math.floor(Quotient).toString() + Radix_Point; // Use floor division to determine the front part of the number immediately
Dividend = Remainder * Base;
// Use long division for the decimal places, so that repeating decimals can be detected
var i = 0;
while (i < p_max + 2) {
if (!(Dividend in Previous_Dividends)) {
Previous_Dividends[Dividend] = i;
Quotient = Dividend / Divisor;
Remainder = Dividend % Divisor;
Dividend = Remainder * Base;
//if (i < p_max) {
Decimal_Digits += Math.floor(Quotient).toString();
//}
console.log('i:', i, 'Quotient:', Quotient, 'Remainder:', Remainder, 'Dividend:', Dividend, 'Result:', Result, 'Decimal_Digits:', Decimal_Digits);
}
else {
RepeatFlag = true;
ApproxFlag = false;
console.log('Repetition Detected');
Prefix = Decimal_Digits.substring(0, Previous_Dividends[Dividend]);
Repetend = Decimal_Digits.substring(Previous_Dividends[Dividend], Decimal_Digits.length);
console.log('Prefix:', Prefix, 'Repetend:', Repetend);
if (Repetend == '0') { // A "repeating" dividend of 0 signals a non-repeating result
console.log('Removing Repetend of 0...');
Repetend = '';
RepeatFlag = false;
Decimal_Digits = Prefix;
}
//Decimal_Digits = Prefix + Repetend;
break;
}
i += 1;
}
if (RepeatFlag == false) {
if (Decimal_Digits.length > p_max) {
console.log('Truncating decimals.');
//Decimal_Digits = Decimal_Digits.substr(0, p_max);
Decimal_Digits = Math.round(parseFloat(Decimal_Digits.substr(0, p_max) + '.' + Decimal_Digits.substr(p_max))).toString();
if (p_max - Decimal_Digits.length >= 0) {
Decimal_Digits = '0'.repeat(p_max - Decimal_Digits.length) + Decimal_Digits;
}
ApproxFlag = true;
}
if (Decimal_Digits.length < p_min) {
console.log('Padding end with zeroes. Decimal Digits:', Decimal_Digits.length, 'p_min:', p_min);
if (p_min - Decimal_Digits.length >= 0) {
Decimal_Digits += '0'.repeat(p_min - Decimal_Digits.length);
}
}
}
console.log('Prefix:', Prefix, 'Repetend:', Repetend);
if (RepeatFlag == true) {
if (Prefix.length + Repetend.length > p_max) {
if (Prefix.length > p_max) {
Prefix = Math.round(parseFloat(Prefix.substr(0, p_max) + '.' + Prefix.substr(p_max))).toString();
if (p_max - Prefix.length >= 0) {
Prefix = '0'.repeat(p_max - Prefix.length) + Prefix;
}
}
else {
Prefix = Prefix + Repetend.substr(0, p_max - Prefix.length);
}
Decimal_Digits = Prefix;
RepeatFlag = false;
ApproxFlag = true;
}
if (Prefix.length + Repetend.length < p_min) {
console.log('Decimal Count is lower than p_min. Prefix length:', Prefix.length, 'Repetend length:', Repetend.length);
if ((p_min - (Prefix.length + Repetend.length)) >= Repetend.length) {
console.log('Multiplying repetend. Remaining space:', p_min - (Prefix.length + Repetend.length), 'Repetend length:', Repetend.length, 'Multiply by:', Math.floor((p_min - (Prefix.length + Repetend.length)) / Repetend.length));
Repetend += Repetend.repeat(Math.floor((p_min - (Prefix.length + Repetend.length)) / Repetend.length));
console.log('New Repetend:', Repetend);
}
while (Prefix.length + Repetend.length < p_min) {
Prefix = Prefix.concat(Repetend[0]);
Repetend = Repetend.slice(1).concat(Repetend[0]);
}
}
}
console.log('Prefix:', Prefix, 'Repetend:', Repetend);
if (RepeatFlag == true) {
if (Repetend.length == 1 && (Prefix.length + Repetend.length < p_max) && RepeatSinglesFlag == true) { Repetend = Repetend.repeat(2); } // Single-digit repetitions will be displayed twice, i.e. 4/3 will result in 1.(33) rather than 1.(3)
Result += Prefix + OL_open + Repetend + OL_close;
}
else {
Result += Decimal_Digits;
}
Result = Sign.concat(Result);
if (ApproxFlag == true) {
Result = Approx.concat(Result);
}
if (Result[Result.length - 1] == Radix_Point) { Result = Result.replace(Radix_Point, ''); }
return Result;
//return Result * Sign;
}
</script>