3

I have found many resources talking about base conversion algorithms and using built in Javascript functions to convert decimal to binary or hex, however I can't find any resources on how to convert to any fractional bases like base 3/2 for instance. The sesquinary number system is quite interesting and I want to be able to use it, but I can't find any way to convert to it from Decimal.

The sesquinary system is as follows: 1, 2, 20, 21, 22, 210, 211, 212, 2100, 2101, etc.

I can't get any reliable way to convert to it. Here is the code I have so far:

function decimalToBase(number, base) {
    let digitArray = [];
    let quotient;
    let remainder;

    for (let i = 0; i < 16; i++) {
        quotient = Math.floor(number / base);
        remainder = number % base;
        digitArray.unshift(remainder);
        number = quotient;

        if (quotient == 0){
            break;
        }
    }
    console.log(digitArray)
}

It works for whole bases under 10, but if I enter a fractional base like 3/2, then I get a result with decimals:

[1, 0.5, 1, 0] // When It should be: [2, 1, 0]

Any help would be greatly appreciated.

Wowkster
  • 197
  • 3
  • 9
  • For one, you can't use `%` for non-integer bases. –  Sep 10 '20 at 22:26
  • @ChrisG what would I use instead? – Wowkster Sep 10 '20 at 22:35
  • @ChrisG I don't understand why that would be an issue – Wowkster Sep 10 '20 at 22:44
  • Hmmm... Usually in a number base system of `n`, the digits range from `0...n-1`. Eg, base `4` has digits `0, 1, 2, & 3`. Seems that with the proposed sesquinary system of base `3 / 2` that the digit `2` is permitted?! What is the logic such that base `3 / 2` is permitted digits `0, 1, & 2`? – Trentium Sep 11 '20 at 00:49
  • @JonTrent That is actually a common misconception. You would be led to believe that the value of the base is the number of symbols but that is actually just a coincidence. Otherwise fractional bases would be impossible, but they are rather mathematically just as sound as integer bases. As far as I am aware, the number of symbols is the value of the numerator (in integer bases the numerator is just the value of the base) since having 3/2 of a numeral would be impossible. – Wowkster Sep 11 '20 at 03:15
  • Never mind, it seems to work fine. –  Sep 11 '20 at 06:28

2 Answers2

2

Using the following resource...

...a proposed solution is...

<html><body>
BaseHi: <input id='hi' value='3' /><br>
BaseLo: <input id='lo' value='2' /><br>
Value:  <input id='val' value='6' /><br>
<button onclick='go()'>Go!</button><br>
<br>
<br>
Result: <span id='result'></span>
<br><br>
Check: <span id='check'></span>

<script>

function decimalToBase( number, baseNum, baseDen ) {

  let digitArray = []
  digitArray[ 0 ] = number;
  let i = 0;
  
  while ( digitArray[ i ] && baseDen < digitArray[ i ] ) {
    let qi = Math.trunc( digitArray[ i ] / baseNum );
    let ri = digitArray[ i ] - qi * baseNum;
    digitArray[ i ] = ri;
    digitArray[ i + 1 ] = ( digitArray[ i + 1 ] || 0 ) + qi * baseDen;
    i++;
  }
  
  digitArray.reverse();
  return digitArray;
  
}

function go() {
  let baseHi = parseInt( document.getElementById( 'hi' ).value );
  let baseLo = parseInt( document.getElementById( 'lo' ).value );
  let value = parseInt( document.getElementById( 'val' ).value );
  if ( baseHi <= baseLo ) {
    document.getElementById( 'result' ).innerHTML = 'Error - BaseHi must be greater than BaseLo';
    return;
  }
  let result = decimalToBase( value, baseHi, baseLo );
  document.getElementById( 'result' ).innerHTML = `${value}<sub>10</sub> = ${result.join( '.' )}<sub>${baseHi}/${baseLo}</sub>`;
  
  let check = '';
  let total = 0;
  for ( let i = result.length - 1, bd = 1; 0 <= i ; i--, bd = bd * baseHi / baseLo ) {
    total += bd * result[ i ];
    check = `${result[i]}*(${baseHi}/${baseLo})<sup>${result.length - i - 1}</sup>` + ( check ? ' + ' : '' ) + check;
  }
  document.getElementById( 'check' ).innerHTML = `${check} = ${total}<sub>10</sub>`;
}
</script>
</body></html>

EDIT: Adjusted answer to permit ease of testing a variety of bases and values, including a cross check to reverse calculate the new base value back to base 10. To my surprise, the algorithm appears to hold true for cases where BaseLo < BaseHi.

Enjoy!

Trentium
  • 3,419
  • 2
  • 12
  • 19
2

For the 3/2 specific case, this article by James Propp exposes a few ways among which one described by Simon Norton in the End Notes:

It’s based on the fact that when n is written as 3k + r, where the remainder r is 0, 1, or 2, the sesquinary representation of n is equal to the sesquinary representation of 2k, with the digit r tacked on at the end. This gives us a quick way to write the sesquinary representation of n from right to left via a process of repeatedly dividing by three, rounding down, and doubling.

go read the full article

They also provide some Mathematica code to run that.

S[n_] := If[n < 3, {n}, Append[S[2 Floor[n/3]], Mod[n, 3]]]

I really don't know Mathematica, so I can't really rewrite it one to one in js, but if I understand correctly the algorithm, that would be something like:

const decimalToSesquinary = (n) => {
  const res = [];
  while (n) {
    const k = Math.floor(n / 3);
    res.push(n % 3);
    n = k * 2;
  }
  return res.reverse().join("");
};

console.log(decimalToSesquinary(6)) // expected "210"
console.log(decimalToSesquinary(7)) // expected "211"
console.log(decimalToSesquinary(8)) // expected "212"
console.log(decimalToSesquinary(100)) // expected "212001201"
Kaiido
  • 123,334
  • 13
  • 219
  • 285