Метод numberToArray()
:
Я некоторое время работал над вашей реализацией и подумал сначала проанализировать метод numberToArray()
.Для начала я решил создать метод для анализа десятичного числа и вернуть statistics
об этом, в основном информацию, которую вы получили из этой части вашего кода:
if (!int) {
let e = ~~a;
d = a - e;
do {
if (d < 1) ++i;
d *= 10;
} while (!Number.isInteger(d));
}
Метод, который у меня естьmade on является следующим (будет использоваться внутри numberToArray()
) и в основном получает следующую информацию:
1) Целочисленная секция (iSection
) десятичного числа (как целое число)).
2) Десятичная часть (dSection
) десятичного числа (в виде целого числа).
3) Количество цифр послеточка (dDigits
).
4) Количество ведущих нулей после точки (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));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
Конечно, если вам не нравится, вы можете поместить эту же логику прямо в метод numberToArray()
.Итак, после выполнения предыдущей функции я провел некоторую реорганизацию вашего кода и добавил несколько комментариев, которые помогут мне понять, что вы делаете.Наконец, и после адаптации вашего кода я обнаружил, что неправильное отображение на массивы было главным образом из-за арифметической точности при работе с числом с плавающей точкой.После некоторого изучения этой проблемы я нашел решение, основанное на математическом correction factor
(оно комментируется кодом при его применении).В общем, и до этого времени я пришел к следующему решению для метода numberToArray()
.
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});
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
Метод arrayToNumber()
:
Для этого я решил пойти самостоятельно (фактически игнорируя вашу текущую логику).Следующий подход будет использовать ранее упомянутый getDecimalStats()
и, в основном, Array :: redu () :
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);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
Наконец, я надеюсь, что вы сможете оценить мои усилия, и, очевидно, в моём решении может быть много улучшений (поэтому любая рекомендация приветствуется).Например, в настоящее время нет ни одного, или нескольких проверок на безопасность.