Это может быть самый простой способ вычислений с десятичными и большими числами в JavaScript.
Как разработчик веб-интерфейса, вы, должно быть, столкнулись со следующей неожиданной проблемой:
Очевидно, что в сообществе открытого исходного кода есть много обходных путей, чтобы справиться с этим. Но теперь я рекомендую довольно простой и элегантный подход, как показано ниже:
BigNumber('0.1 + 0.2') // "0.3"
BigNumber('0.2
* (5 + 3 * 4 - 1) + 0.1') // "
3.3"
Так удивительно? Прежде чем объяснять, как это сделать, я хотел бы объяснить, почему мы сталкиваемся с вышеуказанной проблемой. Если вы знали это или вас не интересуют утомительные объяснения, вы можете сразу перейти к здесь.
TL;DR
Как и многие другие языки, JavaScript также использует IEEE 754 в качестве представления действительных чисел на компьютере. Поэтому количество представленных с помощью JavaScript было ограничено схемой хранения IEEE 754. Подробное объяснение вы можете найти здесь.
Пример 64-битной раскладки:
Десятичное число хранится как бесконечность
Люди используют десятичную систему для подсчета чисел (возможно, потому, что у нас десять пальцев). Однако машина использует двоичный код для представления чисел. Преобразование представленных различных систем счисления является основной причиной. Например, 1/3, представленная тройным числом, равна 0.1
, а представленная десятичным числом — 0.3333333333…
. Это может быть хорошим примером, объясняющим, почему десятичная дробь хранится как бесконечность. Преобразование между другими системами счисления может быть по аналогии с примером, включая десятичную систему счисления в двоичную.
Вышеупомянутая аналогия может быть сложной, это не строгий подход к ее доказательству. Чтобы погрузиться в эту проблему, мы должны знать, что такое система счисления и кардиналы, и использовать математические методы. Если вас интересуют доказательства, смотрите здесь.
Проработай это
bignumber.js — довольно неплохая библиотека для десятичной и недесятичной арифметики произвольной точности. Но его использование не является дружественным к людям, как это:
var BigNumber = require(“bignumber.js”)
// 0.2
* (5 + 3 * 4 - 1) + 0.1
new BigNumber(0.2).times(new BigNumber(5).plus(new BigNumber(3).times(4)).minus(1)).plus(0.1).toString()
Итак, нам нужен код, который поможет нам делать описанные выше ужасные вещи.
Преобразование инфикса в постфикс (TL;DR)
Почему существует эпизод преобразования инфикса в постфикс? Это может быть связано с разным подходом к мышлению между машинами и людьми. Мы человеческая модель мышления, как квант, вы никогда не знаете, что вы получите, но Бог знает, но также есть таинственная сила, чтобы связать эту точку со следующей точкой или эту точку с предыдущей точкой. Но для компьютера, как мы все знаем, есть только две модели мышления — массив и связь. Вот почему очевидно простую арифметическую операцию так сложно решить с помощью программирования.
A * (B + C * D - E) + F
Выше приведена типичная арифметическая операция. Люди могут быстро узнать точку прорыва с первого взгляда. Это могло бы удовлетворить общий умственный интеллект людей — делать шаг за шагом самый простой и высокий приоритет. Но, кажется, бесполезно строить мост между нашей интуицией и программой. Поэтому нам нужно проанализировать нашу интуицию, чтобы выяснить взаимосвязь между ними.
Решение арифметической задачи может быть очевидным и естественным делом, но оно определенно не является априорным. Оно также требует использования опыта и практики снова и снова. Теперь давайте проанализируем наше мышление, чтобы выяснить, что произошло и как это произошло. Независимо от того, как выполняется расчет, всегда существует принцип, что время течет слева направо, что является основой того, что мы делаем все. Остальные символы, такие как +-×÷ ( ), чья миссия состоит в том, чтобы ограничить течение времени, а не изменить его. Давайте вдумаемся в эти символы.
Правило приоритета
Приведенный выше вывод указывает на то, что арифметические расчеты связаны с течением времени, а символы просто обеспечивают разнообразие течения времени. Как эти символы воздействуют на течение времени? Это приоритет в кавычках. Как эти символы различают друг друга?
1.( ) 2. × ÷ 3. + -
Вышеуказанные группы приоритетов символов уменьшаются сверху вниз. И сверстники в каждой группе также придерживаются течения времени. Таким образом, мы просто следим за течением времени слева направо и в соответствии с приоритетом для расчета. Мы также должны знать, что круглые скобки отличаются от других операторов. Скобки не влияют на операнды для формирования нового операнда, они только помечают рекурсивную группу, как и всю арифметическую операцию. Я подробно остановился на подходе к расчету слева направо:
Начните с самого левого символа, чтобы выполнить вычисления в соответствии с правилом старшинства слева направо, каждый операнд будет выполняться с левым или правым, имеющим более высокий приоритет. Когда дело доходит до «)», оглянитесь назад, чтобы найти ближайший «(» и рекурсивно выполните операцию в этой паре скобок.
Алгоритм преобразования инфикса в постфикс
До сих пор самоанализ был завершен. Мы построим мост между людьми и компьютерами. Приведенный выше вывод приемлем для людей и компьютеров. Однако это не строгое описание технических деталей. К счастью, для решения проблем с компьютерной арифметикой обычно используется обходной путь: инфикс-постфикс. Ниже более строго описывается арифметический алгоритм, который был взят из здесь.
Обзор правил:
1. Выводить операнды по мере их поступления.
2. Если стек пуст или содержит левую скобку сверху, поместите входящий оператор в стек.
3. Если входящий символ является левой скобкой, поместите его в стек. стек.
4. Если входящий символ является правой скобкой, извлекайте стек и печатайте операторы, пока не увидите левую скобку. Отбросить пару скобок.
5. Если входящий символ имеет более высокий приоритет, чем вершина стека, поместите его в стек.
6. strong> Если входящий символ имеет такой же приоритет, как и вершина стека, используйте ассоциацию. Если ассоциация слева направо, извлеките и напечатайте вершину стека, а затем нажмите входящий оператор. Если связь справа налево, нажмите входящий оператор.
7. Если входящий символ имеет более низкий приоритет, чем символ на вершине стека, извлеките стек и напечатайте верхнюю часть. оператор. Затем проверьте входящий оператор на новой вершине стека.
8. В конце выражения извлеките и напечатайте все операторы в стеке. (Не должно оставаться скобок.)
Почему СТЕК
Почему должен быть СТЕК? Почему НЕ массив или очередь? Обратите внимание на 3 выше, что означает, что пакетное извлечение будет осуществляться в соответствии с правилом приоритета сверху вниз. Если только приходят с монотонно возрастающими, мы их высовываем, то не будем путаться и строго придерживаться правила старшинства. Это может быть немного сложно, но правильно использует стек, а также следует правилу приоритета.
Ниже приведен типичный пример реализации описанного выше подхода. Выполню поэтапно:
A * (B + C * D - E) + F
Фрагмент кода
Следующий фрагмент кода является реализацией вышеуказанного алгоритма.
Реальные вещи
Объединив bignumber.js, о котором мы упоминали ранее, и алгоритм преобразования инфикса в постфикс, мы, наконец, можем это сделать:
BigNumber('0.2
* (5 + 3 * 4 - 1) + 0.1') // "
3.3"
Есть удобный пакет NPM, который я хотел бы вам порекомендовать. Вы можете попробовать сделать ту же удивительную вещь, что и в начале: https://github.com/licaomeng/bignumber-math-expression