Fwd: Proposal: 1) Number (integer or decimal) to Array 2) Array to Number (integer or decimal)

guest271314 guest271314 at gmail.com
Thu Mar 7 20:42:30 UTC 2019


---------- Forwarded message ---------
From: guest271314 <guest271314 at gmail.com>
Date: Thu, Mar 7, 2019 at 8:35 PM
Subject: Proposal: 1) Number (integer or decimal) to Array 2) Array to
Number (integer or decimal)
To: <es-discuss at mozilla.org>


Original concept: Integer or decimal to array and array to decimal or
integer https://codegolf.meta.stackexchange.com/a/17223

Proof of concept (with bugs)

function numberToArray(n) {

  if (Math.abs(n) == 0 || Math.abs(n) == -0) {
    return [n]
  }

  const r = [];

  let [
    a, int = Number.isInteger(a), d = g = [], e = i = 0
  ] = [ n || this.valueOf()];

  if (!int) {
    let e = ~~a;
    d = a - e;
    do {
      if (d < 1) ++i;
      d *= 10;
    } while (!Number.isInteger(d));
  }

  for (; ~~a; r.unshift(~~(a % 10)), a /= 10);

  if (!int) {
    for (; ~~d; g.unshift(~~(d % 10)), d /= 10);
    g[0] = g[0] * (1 * (10 ** -i))
    r.push(...g);
  }

  return r;
}
function arrayToNumber(a) {
  if ((Math.abs(a[0]) == 0 || Math.abs(a[0]) == -0)
     && a.length == 1) return a[0];
  const [
    g, r = x => x.length == 1
                ? x[0]
                : x.length === 0
                  ? x
                  : x.reduce((a, b) => a + b)
    , b = a.find(x => g(x)), p = a.findIndex(x => g(x))
  ] = [x => !Number.isInteger(x)];

  let [i, j] = [b ? p : a.length, -1];

  return a.length === 1
    ? a[0]
    : b && p
      ? r(a.slice(0, p).map(x => i ? x * (10 ** --i) : x))
        + (a[p] + (a[p + 1] !== undefined
          ? r(a.slice(p + 1).map(x => x * (10 ** --j)))
          : 0))
      : r(a.map(x => i ? x * (10 ** --i) : x))}
let tests = [0, 200, 100.00015, -123, 4.4, 44.44, -0.01, 123
            , 2.718281828459, 321.7000000001, 809.56
            , 1.61803398874989, 1.999, 100.01, 545454.45
            , -7, -83.782, 12, 1.50, 100.0001];
let arrays = tests.map(n => [...numberToArray(n)]);
let numbers = arrays.map(n => arrayToNumber(n));

console.log({tests, arrays, numbers});


and working code (fixed bugs) by Stack Overflow user Shidersz
https://stackoverflow.com/users/10366495/shidersz

Method numberToArray():

I have been working some time on your implementation, and thinked to first
analyze the numberToArray() method. To start, I have decided to create a
method for analyze a decimal number and return statistics about it,
basically, the information you where getting from this part of your code:

if (!int) {
    let e = ~~a;
    d = a - e;
    do {
        if (d < 1) ++i;
        d *= 10;
    } while (!Number.isInteger(d));}

The method I have made on is the next one (will be used inside
numberToArray()) and basically gets the next information:

*1)* Integer section (iSection) of the decimal number (as integer).

*2)* Decimal section (dSection) of the decimal number (as integer).

*3)* Number of digits after the dot (dDigits).

*4)* Number of leading zeros after the dot (dZeros).

function getDecimalStats(dec){
    let dDigits = 0, test = dec, factor = 1, dZeros = 0;

    // Store the integer section of the decimal number.

    let iSection = ~~dec;

    // Get the numbers of digits and zeros after the comma.

    while (!Number.isInteger(test))
    {
        factor = Math.pow(10, ++dDigits);
        test = dec * factor;
        dZeros += Math.abs(test - (iSection * factor)) < 1 ? 1 : 0;
    }

    // Store the decimal section as integer.

    let dSection = test - (iSection * factor);

    // Return an object with all statistics.

    return {iSection, dSection, dZeros, dDigits};};

console.log(getDecimalStats(10.001));
console.log(getDecimalStats(-210.1));
console.log(getDecimalStats(-0.00001));

Of course, if you dislike, you can put this same logic directly inside
numberToArray() method. So, after making the previous function, I have done
some reorganization on your code and added some commentaries to helps me
understand what you where doing. Finally, and after adapted your code, I
have found that the wrong mapping to the arrays was mostly because the
arithmetic precision when operating with float number. After investigate
some time about this problem, I found a solution that is based using a
mathematical correction factor (it is commented on the code when it is
applied). All in all, and until this time, I have come with the next
solution to the numberToArray() method.

function getDecimalStats(dec){
    let dDigits = 0, test = dec, factor = 1, dZeros = 0;

    // Store the integer section of the decimal number.

    let iSection = ~~dec;

    // Get the numbers of digits and zeros after the comma.

    while (!Number.isInteger(test))
    {
        factor = Math.pow(10, ++dDigits);
        test = dec * factor;
        dZeros += Math.abs(test - (iSection * factor)) < 1 ? 1 : 0;
    }

    // Store the decimal section as integer.

    let dSection = test - (iSection * factor);

    // Return an object with all statistics.

    return {iSection, dSection, dZeros, dDigits};};
function numberToArray(n){
    let r = [];

    if (Math.abs(n) == 0)
        return [n];

    let [a, int = Number.isInteger(a), g = []] = [n || this.valueOf()];

    // Get the stats of the decimal number.

    let {dSection, dZeros} = getDecimalStats(a);

    // Push the integer part on the array.

    for (; ~~a; r.unshift(~~(a % 10)), a /= 10);

    // Push the decimal part on the array.

    if (!int)
    {
        // Push decimal digits on temporal array "g".
        for (; ~~dSection; g.unshift(~~(dSection % 10)), dSection /= 10);

        // Define the correction factor for the next operation.
        let cf = 10 ** (++dZeros);

        // Map g[0] to a decimal number and push elements on the array.
        g[0] = (g[0] * cf) * ((10 ** -dZeros) * cf) / (cf * cf);
        r.push(...g);
    }

    return r;}
let tests = [0, 200, 100.00015, -123, 4.4, 44.44, -0.01,
123,2.718281828459, 321.7000000001, 809.56,1.61803398874989, 1.999,
100.01, 545454.45,-7, -83.782, 12, 1.50, 100.0001];
let arrays = tests.map(n => [...numberToArray(n)]);
console.log({tests, arrays});

Method arrayToNumber():

For this one I decided to go on my own (actually ignoring your current
logic). The next approach will use the previously mentioned
getDecimalStats() and mainly the Array::reduce()
<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce>
:

function getDecimalStats(dec){
    let dDigits = 0, test = dec, factor = 1, dZeros = 0;

    // Store the integer section of the decimal number.

    let iSection = ~~dec;

    // Get the numbers of digits and zeros after the comma.

    while (!Number.isInteger(test))
    {
        factor = Math.pow(10, ++dDigits);
        test = dec * factor;
        dZeros += Math.abs(test - (iSection * factor)) < 1 ? 1 : 0;
    }

    // Store the decimal section as integer.

    let dSection = test - (iSection * factor);

    // Return an object with all statistics.

    return {iSection, dSection, dZeros, dDigits};};
function arrayToNumber(a){
    // Get the index of the first decimal number.

    let firstDecIdx = a.findIndex(
        x => Math.abs(x) > 0 && Math.abs(x) < 1
    );

    // Get stats about the previous decimal number.

    let {dZeros} = getDecimalStats(firstDecIdx >= 0 ? a[firstDecIdx] : 0);

    // Normalize firstDecIdx.

    firstDecIdx = firstDecIdx < 0 ? a.length : firstDecIdx;

    // Reduce the array to get the number.

    let number = a.reduce(
        ({num, dIdx, dPow}, n, i) =>
        {
            // Define the correction factor.
            let cf = 10 ** (dPow + i - dIdx);

            if (i < dIdx)
               num += n * (10 ** (dIdx - i - 1));
            else if (i === dIdx)
               num = ((num * cf) + (n * cf)) / cf;
            else
               num = ((num * cf) + n) / cf;

            return {num, dIdx, dPow};
        },
        {num: 0, dIdx: firstDecIdx, dPow: ++dZeros}
    );

    return number.num;}
let tests = [
    [0],
    [2, 0, 0],
    [1, 0, 0, 0.0001, 5],
    [-1, -2, -3],
    [4, 0.4],
    [4, 4, 0.4, 4],
    [-0.01],
    [1, 2, 3],
    [2, 0.7, 1, 8, 2, 8, 1, 8, 2, 8, 4, 5, 9],
    [3, 2, 1, 0.7, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [8, 0, 9, 0.5, 6],
    [1, 0.6, 1, 8, 0, 3, 3, 9, 8, 8, 7, 4, 9, 8, 9],
    [1, 0.9, 9, 9],
    [1, 0, 0, 0.01],
    [5, 4, 5, 4, 5, 4, 0.4, 5, 0],
    [-7],
    [-8,-3, -0.7, -8, -2],
    [1, 2],
    [1, 0.5],
    [1, 0, 0, 0.0001]];
let numbers = tests.map(n => arrayToNumber(n));
console.log(numbers);

Finally, I hope you can value my efforts, and obviously there can be a lot
of improvements to my solution (so, any recommendation is welcome). For
example, there are currently none or few checks for safety.


Number (integer or decimal) to array, array to number (integer or decimal)
without using strings https://stackoverflow.com/q/54433007

*Context and use cases:*

BigInt <https://developers.google.com/web/updates/2018/05/bigint> in
available in some browsers, though not a BigDecimal. The conversion from
integer or decimal to array and array to integer or decimal should be
possible using the JavaScript programming language. The input and output
should not need to be converted to a string during the procedure.

Ability to adjust *nth* digit of an integer or decimal by adjusting decimal
or integer at *nth* index of array, to try to solve OEIS A217626
<https://oeis.org/A217626> directly, for example

~~(128.625*9*1.074)//1243~~(128.625*9*1.144)//1324

where the decimal portion can be manipulated by referencing the index of an
array, then converting the array back to a number.

Specification (WIP):

[...Math.E] -> [2, 0.7, 1, 8, 2, 8, 1, 8, 2, 8, 4, 5, 9] -> 2.718281828459

Input <----------> Output
-123               [-1,-2,-3]4.4                [4,0.4]44.44
   [4,4,0.4,4]-0.01              [-0.01]123                [1,2,3]200
              [2,0,0]2.718281828459
[2,0.7,1,8,2,8,1,8,2,8,4,5,8,9]321.7000000001
[3,2,1,0.7,0,0,0,0,0,0,0,0,1]809.56
[8,0,9,0.5,6]1.61803398874989   [1,0.6,1,8,0,3,3,9,8,8,7,4,9,8,9]1.999
             [1,0.9,9,9]100.01             [1,0,0,0.01]545454.45
   [5,4,5,4,5,4,0.4,5]-7                 [-7]-83.782
[-8,-3,-0.7,-8,-2]1.5                [1,0.5]100.0001
[1,0,0,0.0001]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190307/f615d11f/attachment-0001.html>


More information about the es-discuss mailing list