How to represent money in JavaScript?

How best to represent monetary units (i.e. money) in JavaScript? I know it's not good to use floating point, given the rounding problem, but I don't know what would be the best alternative. Other languages have Decimal types like built-ins, but as far as I know it has nothing standardized (or widely used) for that purpose in JavaScript.

To date, I have used integers to represent cents, and done all formatting / conversion in hand. Is there a better way, or more "clean"?

Additional details: I am seeking a canonical answer - given that this is a common requirement that many of us have to deal with, and that is often done incorrectly. A good answer can be either a library indication-provided well grounded - or a guideline to implement manually (if the right answer is "depends", i.e. does not exist a canonical form for this).

Examples of criteria to consider (not exhaustive):

  1. reliability - does this representation produce correct results in mathematical operations involving monetary units(e.g. correct rounding)? behaves well in borderline cases?
  2. interoperability - it is simple to convert from and to a format accepted by the server and / or external systems (in general, a decimal without a thousand separator and using the dot . to separate the integer part from the fractional)?
  3. robustness - if the user enters the data incorrectly (e.g. using the semicolon incorrectly/inconsistently, putting the cipher or not, etc.) does the conversion to this format interpret the input correctly and/or consistently fail?
  4. presentation - is it simple to format this type of data to be presented to the user?
Author: mgibsonbr, 2014-03-29

7 answers

All the answers add great information, but I would like to give a more direct answer.

Representing a currency (money)

The most suitable way is to use integers (int or long). The reason for avoiding floating point numbers is the binary representation problems that cause differences in values even in very simple operations.

In addition to Fowler (quoted by Leonardo), Bloch also advocates the use of integers in the book Effective Java. Another source is this SOEN question .

The idea here is analogous to representing mass in grams rather than kilograms. In the case of Real, we represent the values in cents instead of real. So, the value R$ 10,99 (ten reals ninety-nine cents) is represented by 1099 (One Thousand ninety-nine cents).

Mathematical and financial operations

The operations of addition, subtraction and multiplication are carried out directly, without any difficulty. However, if there are split operations, one should properly round the final result to integer again.

Rounding is an interesting topic and we cannot be simplistic. The truth is that there is no fixed rule for rounding up or down . When we talk about money, we are not only considering accuracy, but who loses and who wins.

One of the examples of legislation affecting rounding is the case of the Defense code consumer. The amount charged from the consumer must always be rounded down.

Another example used a lot in financial means is the distribution of installments. For example, suppose the calculation of the installments of a financing R$ 100,70 in three times without interest. In terms of legislation, the customer can not pay more than the total amount to be charged. Note that the value divided into three parts is equal to 33,5666... and we cannot round up (R$ 33,57), otherwise the customer will pay the total of R$ 100,71 (one penny more). One solution to this is to round the portion down (R$ 33,56), resulting in a total of R$ 100,68, and the Company assumes the loss of 2 cents.

However, many financial institutions do not want these differences polluting accounting. We must remember that each rounding must be justified countably. As a result, it is common in systems where I worked we have an adjustment in the last installment. Continuing with the example above, we would have the plots R$ 33,57, R$ 33,57 and R$ 33,56, totaling exactly R$ 100,70.

Conversion between different currencies

The conversion of one currency to another can follow the same concept of mathematical operations, only by adjusting the basic average unit.

User presentation

One of the advantages of using integers is to separate the internal representation from the visual presentation.

The integer can be easily formatted using any routine, with the of this SOEN response :

Number.prototype.formatMoney = function(c, d, t){
var n = this, 
    c = isNaN(c = Math.abs(c)) ? 2 : c, 
    d = d == undefined ? "." : d, 
    t = t == undefined ? "," : t, 
    s = n < 0 ? "-" : "", 
    i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", 
    j = (j = i.length) > 3 ? j % 3 : 0;
   return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
 };

Note that the function must be adapted to extract the two decimal places from the number.

Solution considerations

Reliability

The results will always be correct and the solution behaves well in limit cases, since there are no rounding problems. One should observe only the business rules of how and when to round, as mentioned above.

Interoperability

This can be a complex point for any solution, as the number formatted in an HTML input may not be correctly interpreted on the server.

Different technologies and frameworks validate and interpret data differently. The point is that if the numbers are formatted in submit , the server should extract all non-numeric characters and then convert the value to a number entire.

If technology is not a limitation to this, the interoperability of this solution is excellent, even avoiding confusion with semicolons.

Robustness

Robustness with respect to data entry is not directly related to the form of representation.

However, rationing thinking about integers can also help in this regard. Know those systems (as in ATMs), where you enter the full value of the right to left, always specifying the decimal part at the end? Like in this question . It is possible to do the formatting with a plugin, as I did in this fiddle .

Presentation

Item already considered in previous topic. Any formatting routine works, as long as you consider that decimal places are represented in the integer part of the number.

Efficiency

Even when there is a "native" type in the language, such as Decimal or BigDecimal, working with integers is more computationally efficient for obvious reasons.

 71
Author: utluiz, 2017-05-23 12:37:32

Before analyzing your requirements I will add some definitions:

Interoperability

Interoperability is the ability of a system (computerized or not) to communicate from transparent form (or the closest to it) with another system (similar or not). For a system to be considered interoperable, it is very important that it works with standards open or ontologies. Wikipedia

Reliability

Reliability or reliability (systemic definition) is the ability of a person or system to perform and maintain its operation in routine circumstances, as well as in hostile and unexpected circumstances. Wiki .

Robustness

Is the ability of a computer system to handle errors during its execution or the ability of an algorithm to continue to operate despite abnormalities of inputs and calculations. Wiki

Your requirements are next

  1. Reliability in the first place-the data type/form of calculation should produce correct results

    • you will only achieve reliability in the library (aka calculos) if you make a extensive test set across library code. This will prove to reliability.
  2. Interoperability, when communicating with the server (if applicable);

    • JSON is a interoperable standard for communication between Javascript and the server. I would use JSON extensively for communication
  3. Interoperability / robustness of formats

    • Your API must support multiple forms of input format representation. By example "R$ 1.23", "1.23", "1,23","102.123,30" or consistently fail by accepting only the correct form of the format and informing the exact place of the error. I recommend accepting only one format so as not to cause confusion. Remembering the concept of ubiquity as recommended by Evans on DDD
  4. The presentation, how to show the outputs to the user

    • you have a die, and you will have multiple representations. If you have a default algorithm place inside the object, if it has multiple representations you need to isolate this.

How to implement?

For Tests:

  • a testing framework [English OS]

(https://stackoverflow.com/questions/300855/javascript-unit-test-tools-for-tdd / ). O testing framework is a personal choice. Usually an aesthetic issue due to your bee. After choosing it I recommend checking the tests of the framework. For me it is unacceptable that a testing framework does not use itself to test its features. (Something like: "who arose before: the egg or the chicken?") The accounting seems to me to have a good set of tests.

For modeling:

As recommended by Martin Fowler in his book patterns of Enterprise Application Architecture you must use:

  1. an integer type with the amount (1000 = R$ 10,00)
  2. the currency type (Reais or dollars - use the currency code).

You should avoid using any type of floating point as this may cause rounding problems which is what you want to avoid. In the calculations you should always take into account the type of the coin.

Patterns of Enterprise Application Architecture

Most of the time, people want rounded monetary values to the lowest unit of currency, such as cents in the dollar. However, there are times when units fractional values are necessary.

It is important to make it clear with what kind of money you this is working, especially in an application that uses both types. It makes sense to have different types for the two cases, as they behave quite differently with regard to arithmetic.

Money is a value object, so it must have its equality operations and hash code overwritten to be based on current currency and amount.

Money needs arithmetic operations, so that you can use objects give type as easily as it uses numbers. However, arithmetic operations with money have some important differences from operations with numbers.

The most obvious, any addition or subtraction needs to have currency science, so that you need to react if you try to add different types of medas. The simplest, and most common, answer is to treat the addition of incompatible coins with a error.In some more sophisticated situations, you can use the idea of Ward Cunningham of a bag of money. This is an object that contains coins of different types together on an object. This object can then participate in calculation as any money object.

Multiplication and division end up being more complicated due to problems of rounding. When you multiply money, do it with a scalar greatness. If you want to add a 5% rate to an account, multiply it by 0.05 so that you only worry about the multiplication by normal numerical types.

there are more details within the book I can not pass the entire chapter here for questions legal.

For representation:

You have multiple representation strategies. Use the strategy pattern for each strategies of representation.

As indicated by Sergio you can use a list with the settings of each type of currency and uses the default if a different need arises for representation.

And the wheel?

Implementing a robust library for monetary processing is not that simple, mainly because of the special cases. So we have to look for the wheels before recreate them. here has a list of Javascript libraries to deal with money and coins. You need to analyze if they meet your points:

  • Tests extensive

  • Easy communication with JSON or an easy way to create a module in the library that does it.

An example

If you find that the current libraries do not meet your requirements I will leave a suggestion to implement the API at the user level just out of curiosity

You could use JavaScript prototypes to do something like a DSL, as follows

"R$ 1,00".money()
"R$ 1,00".add("R$ 2,00")

Or if you like the style jQuery

$$("R$ 1,00").add("R$ 1,00").to("USD")
//Ou
$$("BRL 1.00").add("BRL 1.00").to("USD")
//Ou
$$("BRL 1.00").usd()

Using the actual monetary format would be best:

$$("R$ 1,00").add("R$ 1,00").to("$").mul(3)

Is an interesting solution if you are going to work only with native Brazilians, otherwise you have to define the nationality in some way. For more than one nationality can use the '$' as a monetary symbol.

 39
Author: Leonardo Otto, 2018-07-25 14:12:12

Found an interesting JSONhere (and possible original here ) that have useful information for a format money. There are 3 aspects that would be interesting to have:

  • How to separate from 999, the DOT is often used.
  • How to separate decimal values. Some coins seem that they do not even have, but the Having does not say how to separate.
  • on which side of the value is the name of the coin. Whether it should be 100£ or £100. I assume it's the second case.

Anyway, using this JSON, here is a function suggestion to wash the format :) I updated the function to accept only numbers and return only strings with format x.xxx,xx, to generate at least consistent data.

function lavarDinheiro(moeda, valor) {
    if (typeof valor != 'number') return false; // para garantir que o input é um numero
    valor = ('' + valor).replace(',', '.');
    valor = ('' + valor).split('.');
    var parteInteira = valor[0] + '';
    var parteDecimal = valor[1];

    // tratar a parte inteira
    var rx = /(\d+)(\d{3})/;
    parteInteira = parteInteira.replace(/^\d+/, function (w) {
        while (rx.test(w)) {
            w = w.replace(rx, '$1.$2');
        }
        return w;
    });

    // tratar a parte decimal
    var formatoDecimal = json[moeda].decimal_digits;

    if (parteDecimal) parteDecimal = parteDecimal.slice(0, formatoDecimal);
    else if (!parteDecimal && formatoDecimal) {
        parteDecimal = '';
        while (parteDecimal.length < formatoDecimal) {
            parteDecimal = '0' + parteDecimal;
        }
    }
    return parteDecimal ? [parteInteira, parteDecimal].join(',') : parteInteira;
}

Example

 18
Author: Sergio, 2014-04-01 04:16:40

The ideal way to represent money in javascript is to use a more complex data structure, which stores the decimal value, the integer value, the comma separator, etc.

There are several small libraries in javascript to manipulate money that go down this path, saving you the worry of having to take care of it manually:

 10
Author: Leonel Sanches da Silva, 2014-04-01 02:06:31

Depends on the size and accuracy of the value you want to deal with:

  • If they are values less than {200 million and accuracy of cents, as is the case with many simple applications. use integers, representing cents, not real. Enter the comma or period only at the time of showing to the user or printing.

  • If they are values greater than $200 million, you will need to create a "Currency" object or similar, which stores the number

  • If they are values with variable accuracy (some in cents, others with 4 decimal places etc) a BigDecimal type Library is the way.

 8
Author: epx, 2014-04-01 14:53:35

Instead of storing the value with decimal separator, why not store the amount of cents for example, that is, just determine the minimum amount of money to be stored, and store the multiples of that minimum unit... so you wouldn't have rounding problems with the calculations.

No input, consider'.'or', ' as a separator, is a good tactic. If the user copies a value from elsewhere, they could consider only the last tab to be the decimal.

In the output, ai would have to configure which format to use, as it depends on the target audience of the system.

 6
Author: Miguel Angelo, 2014-04-01 13:15:15

For those who need a quick fix to solve small problems, follow a few simple javascript functions:

    <script>

    // Abaixo seguem 3 funções genéricas que podem ser utilizadas em qualquer lugar


    // Converte   [valor texto com vírgula para  centavos]    para    [float]

    function textoParaFloat(texto)
    {
        // Retira pontos que separam milhar se existirem. Ex: 1.000,25 vira 1000,25
        texto = texto.replace("." , "");

        // Substitui vírgula separando a casa decimal por ponto ex: 1000,25 vira 1000.25
        texto = texto.replace("," , "."); // isso é necessário para converter para float corretamente

        // Converte de texto para número real
        numero = parseFloat(texto);

        return numero;  // Retorna um número float para ser usado para fazer cálculos    
    }



    // Converte   [valor float com ponto para casa decimal]  para  [texto com vírgula para separar centavos]

    function floatParaTexto(numero)
    {
        numero = numero.toFixed(2); // Duas casas decimais

        texto = numero.toString(); 
        texto = texto.replace("." , ","); // substitui a vírgula por ponto 

        return texto;
    }



    // Apenas prevenção para pessoas que digitam ponto de milhar por costume
    function removePonto(x)
    {
        x.value = x.value.replace('.', '');
    }

    </script>





    Valor: <input type="text" name="valor" id="valor" onkeyup="removePonto(this)"><br>
    <small>Digite apenas números e vírgula para separar os centavos</small>
    <br><br>

    Qtd:   <input type="text" name="qtd"   id="qtd"><br>
    <small>Digite apenas números</small>
    <br><br>

    <input type="button" value="Calcular" onclick="testar();">
    <br><br>
    
    Valor calculado: <span id="teste"></span>




    <script>
    // Essa função abaixo testa as funções genéricas declaradas no bloco de script acima, com os valores digitados

    function testar()
    {
        valor_texto = document.getElementById("valor").value;

        // Convertendo valor_texto com vírgula  para  float  para poder usar em cálculo
        valor_float = textoParaFloat(valor_texto);

        qtd = document.getElementById("qtd").value;
        qtd = parseInt(qtd);

        valor_vezes_qtd = valor_float * qtd; // Esse é um valor númerico calculado

        // converte o valor multiplicado para texto com vírgula
        valor_multiplicado_texto = floatParaTexto(valor_vezes_qtd);

        //exibe o valor multiplicado na tela
        document.getElementById("teste").innerHTML = valor_multiplicado_texto;
    }
    </script>
 2
Author: Antonio Alexandre, 2017-01-06 19:38:00