Estudo e validações de bibliotecas matemáticas do JavaScript

Estudo e validações de bibliotecas matemáticas do JavaScript

Contextualização

Esse artigo é o resultado de um dos nossos projetos realizados no Vórtx Next que é nosso programa de estágio de férias, e pretende dissertar acerca da execução de cálculos matemáticos utilizando o Node.js.

O Node.js utiliza por de baixo dos panos o interpretador V8 para execução do JavaScript e, por conta disso, todos os números são codificados como números de ponto flutuante de precisão dupla, seguindo o padrão internacional IEEE 754.

TL;DR: O JavaScript possui problemas com pontos flutuantes, principalmente em relação a casas decimais de alta precisão.

Este formato armazena números em 64 bits, sendo que o número, a fração (AKA mantissa), é armazenado nos bits 0 a 51, o expoente nos bits 52 a 62 e o sinal no bit 63.

Os números de ponto flutuante são representados como frações binárias. Infelizmente, a maioria das frações decimais não pode ser representada exatamente como frações binárias. Os números de ponto flutuante decimais inseridos são aproximados apenas pelos números de ponto flutuante binários realmente armazenados na máquina. Dito isso, você verá que a aritmética de ponto flutuante NÃO é 100% precisa.

Iremos ver que quando utilizamos o JavaScript para trabalhar com números, sejam eles cálculos simples, como é o caso das adições (I) e (II) ou mais complexos, a linguagem possui certas imprecisões em suas operações.


0.2 + 0.1
//result: 0.30000000000000004

0.3 - 0.1 
//result: 0.19999999999999998

Como é de se imaginar, os arredondamentos e equívocos podem resultar em valores muitos díspares do esperado, afetando as aplicações financeiras uma vez que essas envolvem diversas etapas de cálculos e números com várias casas decimais. Desse modo, nesse artigo vamos nos debruçar sobre alternativas e tentativas de resolução para esse problema, assim como ponderar se a linguagem é a mais adequada para lidar com tais números.

Metodologia

Para entendermos melhor o comportamento do JavaScript e mapearmos o problema com propriedade, seguimos uma série de etapas e processos para chegarmos à conclusão.

Primeiramente, foram pesquisados e levantados alguns cálculos de maior complexidade que podem ser usados como testes.

Ao elencar as bibliotecas acima, pesquisamos muito antes da escolha dos cálculos e das bibliotecas que utilizaríamos:

Em seguida, definimos algumas bibliotecas que poderiam potencialmente solucionar o problema da precisão com seus métodos próprios. Idealmente, essas bibliotecas deveriam possuir uma maior fidelidade aos valores de nossa referência do que o retorno do JavaScript nativo, sendo uma alternativa a este.

Por fim, foi realizada a comparação entre as bibliotecas para chegarmos a alguma conclusão acerca da problemática.

Aplicação do problema

A partir da definição e organização do problema, fomos em busca de cálculos complexos e preferencialmente, voltados à área financeira que intrinsecamente fazem parte do nosso dia a dia.

Cálculo do Preço Unitário (PU)

O primeiro cálculo analisado foi o de PU, que devido ao número de casas decimais e operações envolvidas, parecia ser um bom início para a avalição do JavaScript.

O PU do título do dia é obtido através da seguinte conta (III), sendo o Valor da Face referente ao título no seu vencimento, o du os dias úteis e a taxa sendo a taxa de juros desse.

Porém, existem dois tipos de cálculo de PU, sendo estes o pré-fixado e o pós-fixado, com respectivas particularidades, ambos testados em nosso programa.

PU pós-fixado

Como utilizamos o Excel como referência, seguimos essa sequência para obter o PU pós-fixado.

PU pré-fixado

Já o PU pré-fixado foi obtido da seguinte maneira:

Após a estruturação da fórmula, colocamos a massa de testes com os valores de média CDI, somente no PU pós-fixado, e os valores esperados de PU em cada caso.

Depois, criamos um arquivo teste para avaliar se o JavaScript nativo passaria nos cenários colocados, sendo esses o cálculo de PU a partir de um valor unitário para os parâmetros da função e com a massa de teste com vários valores finais esperados. O JavaScript somente passou no teste do cálculo de PU pré-fixado utilizando os vários valores da massa de teste, enquanto no resto ele retornou valores diferentes aos previstos. Em alguns casos, a diferença estava em uma casa decimal e, em outros em uma unidade. No caso, estávamos trabalhando com 14 casas decimais.

Quando analisamos minunciosamente as linhas de código, percebemos que o programa estava com uma certa imprecisão na obtenção do fatorDI, o que nos obrigou a testar métodos como toFixed() para limitar as casas decimais ou até mesmo a função externa floorFigure().

Implementação da fórmula do pós-fixado

Abaixo, observa-se a função criada para realizar o cálculo de PU de um título Pós Fixado no JavaScript nativo e os métodos auxiliares toFixed() e floorFigure():

function calculoPuPos(mediaCdi, porcentagem, dp, fatorDiAcumuladoAnterior, vne) {

    const spread = calculoFatorSpread(porcentagem, dp)
    const di = calculoFatorDi(mediaCdi)
    const diAcumulado = fatorDiAcumuladoAnterior * di
    const juros = diAcumulado * spread
    const pu = vne * juros

    const spreadToFixed = calculoFatorSpread(porcentagem, dp).toFixed(14)
    const diToFixed = calculoFatorDi(mediaCdi).toFixed(14)
    const diAcumuladoToFixed = (fatorDiAcumuladoAnterior * diToFixed).toFixed(14)
    const jurosToFixed = (diAcumuladoToFixed * spreadToFixed).toFixed(14)
    const puToFixed = (vne * jurosToFixed).toFixed(10)

    const spreadFloorFigure = floorFigure(calculoFatorSpread(porcentagem, dp), 14)
    const diFloorFigure = floorFigure(calculoFatorDi(mediaCdi), 14)
    const diAcumuladoFloor = floorFigure((fatorDiAcumuladoAnterior * diFloorFigure), 14)
    const jurosFloor = floorFigure((diAcumuladoFloor * spreadFloorFigure), 14)
    const puFloorFigure = floorFigure((vne * jurosFloor), 10)
    
    return {
        toFixed: {
            spread: Number(spreadToFixed),
            di: Number(diToFixed),
            diAcumulado: Number(diAcumuladoToFixed),
            fatorJuros: Number(jurosToFixed),
            pu: Number(puToFixed)
        },
        floorFigure: {
            spread: Number(spreadFloorFigure),
            di: Number(diFloorFigure),
            diAcumulado: Number(diAcumuladoFloor),
            fatorJuros: Number(jurosFloor),
            pu: Number(puFloorFigure),
        },
        nativo: {
            spread: spread,
            di: di,
            diAcumulado: diAcumulado,
            fatorJuros: juros,
            pu: pu,
        }
    }
}

function calculoFatorDi(mediaCdi) {
    const umPorDoisCincoDois = (1 / 252)
    const mediaCdiPorCem = ((mediaCdi / 100) + 1)
    const fatorDI = Math.pow(mediaCdiPorCem, umPorDoisCincoDois)

    return fatorDI
}

function calculoFatorSpread(porcentagem, dp) {
    const dpPorDoisCincoDois = (dp / 252)
    const porcentagemMaisUm = (1 + porcentagem)
    const fatorSpread = Math.pow(porcentagemMaisUm, dpPorDoisCincoDois)

    return fatorSpread
}

Após a execução do código acima, teremos o resultado semelhante ao print abaixo:

Analisando os dados da execução única mais a fundo, percebemos algumas diferenças entre cada cálculo realizado para obter o valor do PU final (no caso do acima seria 10144.9266661423), onde todos trouxeram resultados diferentes seja em alguma casa decimal ou em unidades maiores. É possível atribuir essa disparidade à presença de operações mais complexas e ao número de casas decimais fornecido na massa de teste, fora as limitações inseridas no Excel. Além disso, em geral, a implementação utilizando a método toFixed() já sinalizava bons sinais de algum modo já que em sua maioria, os resultados com ela estão corretos ou aproximados, que no caso o valor do PU final estava conforme o esperado.

Para validar ainda mais essa teoria, decidimos então executar o cálculo com uma massa para o período de um mês, os resultados foram os seguintes (considerar index+1 como dia):

Após a aplicação de todos esses cálculos e os testes que realizamos, provou-se que o JavaScript realmente enfrenta dificuldades quando se trata de operações difíceis com muitas casas decimais. Existem workarounds, mas ele sozinho apresentou imprecisões nas casas decimais mais altas. Dessa forma, tornou-se necessário o uso de métodos e funções externas e muitos tratamentos em cada cálculo para conseguir equivaler o valor com o de nossa referência e isso foi possível, sendo a implementação com o toFixed() mais precisa e acertando em todos os casos diferente da implementação com o floorFigure() que apresentou imprecisões nas casas decimais mais altas.

Sendo assim, procuramos soluções que pudessem simplificar o desenvolvimento dos cálculos aumentando a precisão sem que tenhamos que implementar muitos workarounds para chegar ao valor final consistente.

Tentativas de resolução

Frente à imprecisão da linguagem, resolvemos utilizar as libs numéricas mais recomendadas e completas para serem potenciais soluções à questão. Depois de pesquisa nos repositórios, chegamos a três libs que poderiam ser interessantes para testar os tipos de cálculos levantados.

Big.js

A lib Big.js foi desenvolvida por MikeMcl e é considerada uma pequena e rápida biblioteca de JavaScript para aritmética decimal de precisão arbitrária, e tem algumas diferenças e alternativas para os operadores nativos.

Após a instalação, foram realizadas algumas alterações no código nativo, já que era preciso converter todos os números usados nas contas para o tipo Big e retornar valor do tipo Number para ser compatível aos do Excel. Além disso tivemos que usar os operadores: times, plus, div.

Um exemplo de aplicação da lib está abaixo no cálculo de PU pós-fixado:


const um = new Big(1);
const doisCincoDois = new Big(252);
const cem = new Big(100);

function calculoPuPos(mediaCdi, porcentagem, dp, fatorDiAcumulado, vne) {

    const mediaCdiB = new Big(mediaCdi);
    const porcentagemB = new Big(porcentagem);
    const dpB = new Big(dp);
    const fatorDiAcumuladoB = new Big(fatorDiAcumulado);
    const vneB = new Big(vne);

    const di = calculoFatorDi(mediaCdiB);
    const spread = calculoFatorSpread(porcentagemB, dpB);
    const diAcumulado = fatorDiAcumuladoB.times(di);
    const fatorJuros = diAcumulado.times(spread);

    const pu = (vneB).times(fatorJuros);


    return {
        spread: spread.round(14, Big.roundDown).toNumber(),
        di: di.round(14, Big.roundUp).toNumber(),
        diAcumulado: diAcumulado.round(14, Big.roundUp).toNumber(),
        fatorJuros: fatorJuros.round(14, Big.roundUp).toNumber(),
        pu: pu.round(10, Big.roundUp).toNumber(),
    }
}

function calculoFatorDi(mediaCdiB) {
    mediaCdiB = typeof mediaCdiB === 'number' ? new Big(mediaCdiB) : mediaCdiB

    const div1por252 = um.div(doisCincoDois)
    const taxa = ((mediaCdiB.div(cem)).plus(um))
    return new Big(Math.pow(taxa, div1por252.toPrecision()))
}

function calculoFatorSpread(porcentagemB, dpB) {
    const divisaoDp = dpB.div(doisCincoDois);

    return new Big(Math.pow((um.plus(porcentagemB)), divisaoDp.toPrecision()));
}

Após execução com os mesmos parâmetros da implementação nativa, o resultado obtido foi:

Assim, observa-se que ela difere de nossa referência na maioria dos resultados, porém exigiu quase nenhum tratamento para chegar ao resultado do pu esperado apesar dos outros fatores do cálculo serem diferentes da execução nativa com ou sem o toFixed() em cada cálculo.

Porém, ao executar o cálculo por um período de um mês, foi visto que o resultado não se mantém em um padrão, e logo o valor do PU começa a se diferenciar muito do resultado esperado:

BigNumber e Decimal

A lib BigNumber.js do mesmo desenvolvedor da lib Big.js, é uma biblioteca de JavaScript para aritmética decimal e não decimal de precisão arbitrária. Também possui algumas diferenças e alternativas para os operadores nativos.

Quando configurada, tivemos que alterar todos os tipos de valores para o type BigNumber e usamos muitos métodos do JavaScript nativo, pois alguns operadores da lib não aceitavam números não inteiros, por exemplo. Desse modo, devido à sua complexidade, resolvemos trocá-la pela lib Decimal, também provida por MikeMcl (detalhes sobre a diferença das libs).

big.js: minimalist; easy-to-use; precision specified in decimal places; precision applied to division only; 4 rounding modes.
bignumber.js: configuration options; NaN; Infinity; precision specified in decimal places; precision applied to division only; random numbers; base conversion; 9 rounding modes; modulo modes; modular exponentiation.
decimal.js: configuration options; NaN; Infinity; non-integer powers, exp, ln, log; trigonometric functions; precision specified in significant digits; precision always applied; random numbers; 9 rounding modes; modulo modes; binary, octal, and hexadecimal; binary exponential notation.

Com a lib Decimal, também tivemos que realizar a conversão dos valores para Decimal, mas todos os operadores funcionavam e eram próprios da biblioteca.

Um exemplo de aplicação da lib Decimal está abaixo no cálculo de PU pós-fixado:

const um = new Decimal(1)

function calculoPuPos(mediaCdi, porcentagem, dp, fatorDiAcumuladoAnterior, vne) {

    const mediaCDIDecimal = new Decimal(mediaCdi)
    const porcentagemDecimal = new Decimal(porcentagem)
    const DpDecimal = new Decimal(dp)
    const fatorDiAcumuladoDecimal = new Decimal(fatorDiAcumuladoAnterior)
    const vneDecimal = new Decimal(vne)

    const di = calculoFatorDi(mediaCDIDecimal)
    const spread = calculoFatorSpread(porcentagemDecimal, DpDecimal)
    const diAcumulado = fatorDiAcumuladoDecimal.times(di)
    const fatorJuros = diAcumulado.times(spread)

    const pu = vneDecimal.times(fatorJuros)

    return {
        spread: spread.toDecimalPlaces(14, Decimal.ROUND_DOWN).toNumber(),
        di: di.toDecimalPlaces(14, Decimal.ROUND_UP).toNumber(),
        diAcumulado: diAcumulado.toDecimalPlaces(14, Decimal.ROUND_UP).toNumber(),
        fatorJuros: fatorJuros.toDecimalPlaces(14, Decimal.ROUND_UP).toNumber(),
        pu: pu.toDecimalPlaces(10, Decimal.ROUND_UP).toNumber(),
    }
}

function calculoFatorDi(mediaCdiB) {

    if (!(mediaCdiB instanceof Decimal)) mediaCdiB = new Decimal(mediaCdiB)

    const doisCincoDois = new Decimal(252)
    const cem = new Decimal(100)
    const div1por252 = (um.dividedBy(doisCincoDois))
    return ((mediaCdiB.dividedBy(cem)).plus(um)).pow(div1por252)
}

function calculoFatorSpread(porcentagemDecimal, dpB) {
    const doisCincoDois = new Decimal(252)
    const divisaoDp = new Decimal(dpB.dividedBy(doisCincoDois))
    return new Decimal(um.plus(porcentagemDecimal)).pow(divisaoDp)
}

Após execução com os parâmetros padrões tivemos o seguinte resultado:

Em relação aos resultados, como pode ser observado na imagem acima, a lib Decimal foi mais precisa já que diversas casas decimais eram equivalentes às da referência, mas também precisou de alguns ajustes para chegar no valor desejado, e foi necessário utilizar o método toDecimalPlaces() com arredondamento para cima, porém, isso apenas foi necessário no retorno da função, onde o cálculo em si não precisou de nenhum tratamento alternativo fora a sua construção.

Quando passamos a executar o cálculo por um período de um mês, percebemos que muitas vezes o valor não deu certo, apesar da lib quase não precisar de ajustes, ela também não se mostrou constante com os resultados, independente se o arredondamento foi para cima ou baixo:

Math

A lib Math.js foi desenvolvida por Jos de Jong, possui várias funções embutidas e trabalha com diversos tipos de dados como números grandes, complexos, frações entre outros, não precisando realizar a conversão dos dados como nas outras libs. Então os operadores utilizados no código foram: multiply, add, divide, pow.

Um exemplo de aplicação da lib está abaixo no cálculo de PU pós-fixado:

function calculoPuPos(mediaCdi, porcentagem, dp, fatorDiAcumulado, vne) {

    const di = calculoFatorDi(mediaCdi)
    const spread = calculoFatorSpread(porcentagem, dp)
    const diAcumulado = multiply(fatorDiAcumulado, di)
    const fatorJuros = multiply(diAcumulado, spread)

    const pu = multiply(vne, fatorJuros)
    return {
        spread: round(spread, 14),
        di: round(di, 14),
        diAcumulado: round(diAcumulado, 14),
        fatorJuros: round(fatorJuros, 14),
        pu: round(pu, 10),
    }
}

function calculoFatorDi(mediaCdi) {
    return pow((add((divide(mediaCdi, 100)), 1)), (divide(1, 252)))
}

function calculoFatorSpread(porcentagem, dp) {
    return pow((add(1, porcentagem)), divide(dp, 252))
}

Sem dúvidas, essa foi a implementação mais limpa entre todas, porém, o resultado não foi bem o esperado. O print a baixo demonstra o resultado após execução que foi utilizando o método round() para definir o tamanho máximo de casas decimais, que passou muito próximo de calcular exatamente o valor esperado porém, diferente da lib decimal, não é possível definir a direção do arredondamento:

Ao executar os testes por período, os resultados não foram muito diferentes:

As-Big

A lib As-Big, foi desenvolvida por Tomas Tulka, e com várias contribuições do mesmo MikeMcl, porém, ela escrita em AssemblyScript, que utiliza WebAssembly por baixo dos panos, o que faz com que uma biblioteca utilize ainda mais o poder de recurso computacional com ainda mais performance.

Abaixo, temos o exemplo da implementação dos cálculos de PU pós fixado. Note que diferente dos cálculos anteriores o código foi escrito na linguagem Assemblyscript, que possui sintaxe similar ao TypeScript, com classes, objetos e atributos de classe:

const cem :Big = Big.of(100)

class puPosResult {
    spreadEsperado: number
    diEsperado: number
    diAcumuladoEsperado: number
    fatorJurosEsperado: number
    puEsperado: number
}

export function calculoPuPos(mediaCDI: f64, porcentagem: f64, dp: i32, DiAcomuladoAnterior: f64, vne: f64) :puPosResult {

    const DI : Big = calculoFatorDi(mediaCDI)
    const spread : Big = calculoFatorSpread(porcentagem, dp)
    const acomuladoDIA : Big  = Big.of(DiAcomuladoAnterior).times(DI)
    const fJuros : Big  = acomuladoDIA.times(spread)
    const pu : Big = Big.of(vne).times(fJuros)
    
    return {
        spreadEsperado: spread.toNumber(),
        diEsperado:  DI.toNumber(),
        diAcumuladoEsperado: acomuladoDIA.toNumber(),
        fatorJurosEsperado: fJuros.toNumber(),
        puEsperado: pu.toNumber()
    }
}

function calculoFatorDi(mediaCDI: f64):Big {            
    const div1por252: f64 = 1 / 252
    const mediaCdiPorCem: Big = Big.of(mediaCDI).div(cem).plus(Big.ONE)
    const fatorDi : Big = Big.of(Math.pow(mediaCdiPorCem.toNumber(), div1por252))
    
    return fatorDi
}

function calculoFatorSpread(porcentagem: f64, dp: f64):Big {    
    const dpPorDoisCincoDois :f64 = dp / 252
    const porcentagemMais1 : Big = Big.ONE.plus(porcentagem)
    const spread :Big = Big.of(Math.pow(porcentagemMais1.toNumber(), dpPorDoisCincoDois))

    return spread
}

Note, que na implementação acima, tivemos o mesmo problema que nas libs anteriores com exceção da Decimal, que para realizar potência com números decimais foi necessário utilizar o método pow() da lib Math  nativa pois a lib Big não dá suporte a esse tipo de operação.

Porém, ao executarmos a função utilizando os mesmos parâmetros padrões exportando a função para o Javascript, não obtivemos um resultado, e a própria lib retornou que o número ultrapassava o limite do f64 do AssemblyScript, porém ao analisar a mensagem de erro, percebemos que o número é o mais preciso de todos, independente do arredondamento, assim, a implementação se mostrou bem promissora.

Então, procuramos questionar o próprio Tomas Tulka se nossa abordagem fazia sentido no contexto da própria As-Big, e sua resposta foi a seguinte:

I guess the problem is that in cases when your Big instance is out of the JS float range you cannot simply convert that Big instance into a float number. If you do so, the floating-point will overflow and you get a wrong 'completely different' number. It means converting Big to a number is not a "safe" operation.

"Acredito que o problema está quando a sua instância do Big está fora do tamanho máximo do JS float, você não pode simplesmente converter um instância Big para float number, se tentar, o float vai estourar e você vai obter um 'número completamente diferente'. Isso significa que converter um Big para Number não é uma operação 'Segura'.'"

Tendo nota, entendemos que tentar utilizar as tecnologias em paralelo, não seria das melhores práticas, então decidimos utilizar a implementação inteira dos testes direto do AssemblyScript, e os resultados foram bem mais promissores apesar da grande curva de aprendizagem que tivemos para implementar. O exemplo abaixo demostra os resultados:

Novamente foram utilizados métodos para realizar a diminuição das casas decimais e, muito semelhante a lib Decimal os valores foram arredondados para cima, porém, a mesma consistência não se manteve ao analisarmos o período de um mês:

Novamente os resultados foram bem parecidos com a implementação da lib Decimal, porém com mais divergências do que a Decimal. Porém, ao tentar realizar testes mais robustos com diversos tipos de comparação visando alguma forma de reduzir as casas decimais, enfrentamos o problema com o próprio AssymblyScript, que ainda está em uma fase muito inicial (utilizamos a v0.19), e por exemplo, ainda não é possível utilizar o try catch entre outras features comuns do JavaScript. O AssemblyScript é bastante promissor, mas não está pronto ainda para ser utilizado "de igual para igual" com o JavaScript neste cenário específico.

Comparação entre as bibliotecas

Assim, com todas as libs implementadas e testadas chegamos à conclusão de que a lib que exige menos mudanças, ajustes ao código e métodos auxiliares, além de ser mais fiel aos valores de referência é a Decimal.

Para dar continuidade à comparação, realizamos o benchmark de tempo para realizar o cálculo de PU pós fixado e pré-fixado. Notamos que em todos os casos a lib mais lenta para realizá-los é a Decimal, levando quase o dobro do tempo, como está exposto no gráfico abaixo, porém, se compararmos a implementação na as-big com o JavaScript nativo, vamos perceber que a performance é bem parecida, isso devido a própria característica da ferramenta, devido ao seu código já ser compilado e optimizado para execução.

Conclui-se, então, que por mais que seja a lib mais precisa e fácil de trabalhar, ela tem sua performance afetada pelo tempo de execução elevado, o que pode ser comprometedor em cálculos extensos.

Estudos que rolaram em paralelo

Ao elencar as bibliotecas acimas, pesquisamos também como outras linguagens como o C#, que possui uma lib matemática muito confiável já consolidada, iria realizar essas implementações. Encontramos algumas dificuldades similares, devido ao mesmo fato que por padrão as linguagens não vão considerar cálculos com 10, 12, 16 casas decimais...

Conclusão e resultados

O desenvolvimento dos cálculos e entender os seus testes foram um grande processo de aprendizado, não apenas por aprender como aplicar diversas regras de diferentes formas, mas também para entender o funcionamento do JavaScript mais a fundo.

Dito isso, concluímos que realmente é possível utilizar JavaScript para cálculos complexos que temos no mercado financeiro, porém, exige uma curva de aprendizagem e uma boa criatividade para solucionar alguns contrapontos que podem aparecer no caminho.

Também, acreditamos que o uso do AssemblyScript é promissor, porém na data de publicação desse artigo, a sua implementação ainda não está completa, não possuindo ferramentas robustas para debug ou até mesmo suporte para try catch, porém devido à sua performance, ela pode vir a ser uma grande aliada ao lidarmos com números complexos e de alta precisão desde que o projeto seja inteiramente implementado em AssemblyScript, principalmente com o amadurecimento da ferramenta e a sua implementação em diversas plataformas.

Disponibilizamos aqui o repositório do projeto: https://github.com/vortx-dtvm/VortxNext-LibsMatematicas-js e te convidamos a realizar mais testes conosco.


Documentação das lib utilizadas:
BigJs: https://mikemcl.github.io/big.js/
BigNumber: https://mikemcl.github.io/bignumber.js/
Decimal: https://mikemcl.github.io/decimal.js/
MathJs: https://mathjs.org
As-Big: https://github.com/ttulka/as-big

AssemblyScript: Implementation status | The AssemblyScript Book


Referências