0

Note: numbers which I'm talking about are currency, thus don't have more than two digits in fractional part.

I've tested 4 libraries (Decimal.js, Numeral.js, Big.js and Math.js) and a simple plain javascript implementation function (which uses Math.round for cutting off fractional part) with two arithmetic operations:

  • 0.55 * 100 => 55 // but it gives 55.00000000000001
  • 0.2 + 0.1 => 0.3 // but it gives 0.30000000000000004

Test script (node v10.4.1, Ubuntu 16.04LTS)

const { Decimal } = require('decimal.js')
const numeral = require('numeral')
const Big = require('big.js')
const math = require('mathjs')

const assert = require('assert')

function roundMultiply () {
  return Math.round(0.55 * 100)
}

function roundAdd () {
  return Math.round((0.2 + 0.1) * 100) / 100
}


function decimalMultiply () {
  return new Decimal(0.55).mul(new Decimal(100))
}

function decimalAdd () {
  return new Decimal(0.2).add(new Decimal(0.1))
}


function numeralMultiply () {
  return numeral(0.55).multiply(100).value()
}

function numeralAdd () {
  return numeral(0.2).add(0.1).value()
}


function bigJsMultiply () {
  return +(new Big(0.55).times(100))
}

function bigJsAdd () {
  return +(new Big(0.2).add(0.1))
}


function mathJsMultiply () {
  return math.number(math.multiply(math.fraction(0.55), 100))
}

function mathJsAdd () {
  return math.number(math.add(math.fraction(0.2), math.fraction(0.1)))
}

function test (fun, funName) {
  console.time(funName)

  for (let i = 0; i < 100000; i++) {
    fun()
  }

  console.timeEnd(funName)
}

console.log('test multiplication results...')

assert(String(roundMultiply()) === '55')
assert(String(decimalMultiply()) === '55')
assert(String(numeralMultiply()) === '55')
assert(String(bigJsMultiply()) === '55')
assert(String(mathJsMultiply()) === '55')

console.log('test multiplication performance...')

test(roundMultiply, 'roundMultiply')
test(decimalMultiply, 'decimalMultiply')
test(numeralMultiply, 'withNumeral')
test(bigJsMultiply, 'withBigJs')
test(mathJsMultiply, 'mathJsMultiply')

console.log('test adding results...')

assert(String(roundAdd()) === '0.3')
assert(String(decimalAdd()) === '0.3')
assert(String(numeralAdd()) === '0.3')
assert(String(bigJsAdd()) === '0.3')
assert(String(mathJsAdd()) === '0.3')

console.log('test adding performance...')

test(roundAdd, 'roundAdd')
test(decimalAdd, 'decimalAdd')
test(numeralAdd, 'numeralAdd')
test(bigJsAdd, 'bigJsAdd')
test(mathJsAdd, 'mathJsAdd')

Results

roundMultiply: 2.673ms
withBigJs: 123.759ms
decimalMultiply: 172.068ms
withNumeral: 206.626ms
mathJsMultiply: 255.870ms

roundAdd: 2.317ms
mathJsAdd: 68.212ms
numeralAdd: 139.752ms
decimalAdd: 184.210ms
bigJsAdd: 222.685ms

As you can see, roundAdd and roundMultiply are far ahead of all other functions.

My main question: are there operands having at most two digits in fractional part and such, which, when peforming simple arithmetic operations (+, -, *, /) with them, whould give incorrect result when using Math.round? Because I can't see any reason why would I need to use any of those (or other libraries) taking into account their performance.

humkins
  • 9,635
  • 11
  • 57
  • 75
  • Possible duplicate of [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – duffymo Nov 13 '18 at 15:04
  • 4
    @duffymo Come on, guys, this site becomes useless because each question is similar to some another question or contains similar keys in its subject or description. Do you really think that because that question is also about binary arithmetic inaccuracy there is no doubt that it contains answers will I ever be faced with errors because of `Math.round` usage instead of special JS library? Well, may be you think my question has not the best subject, in that case can you please suggest better one or what I should improve in it? Thanks – humkins Nov 13 '18 at 18:54

0 Answers0