I have a huge problem with Javascript decimal rounding - javascript

After doing a lot of looking around, I don't have to describe the precision problem that is apparent in the coding community. However, most solutions rely on converting the number to a string. In my case, that is not an option since I am using an API which requires the passed in number to be well.. a number. And every time I get the imperfect precision the API shoots out an error breaking my application's functionality. Simply catching the error is not really a solution, since I still have to submit the value and the value will not be changing. Here's what I am currently using and it seems to work for the most part, but every once in a while I'm not so lucky, if anyone has an idea of how to work around this all comments will be appreciated!
let rounded = Math.round((n + Number.EPSILON) * Math.round(Math.pow(10, precision))) / Math.round(Math.pow(10, precision))
let fixed = rounded.toFixed(precision)
return Number(fixed)
Side note, both rounded and fixed can cause the same issue, I've tried it both ways, and desired precision is from 2 to 8 depends on the circumstance, I've also tried it with and without EPSILON, I've tried truncating, rounding, nothing seems to work, and I need a number at the end not a string...

decimal.js
Use decimal.js for mathematically precise computations. Similar solutions are available.
Your issues are a known fact about using floating point numbers in programming in general.
You can find more help and tutorials about decimal.js online. Have a look:
https://github.com/MikeMcl/decimal.js/

Related

Best practice to handle rounding error in javascript

i have issue with floating point rounding in javascript. As far as i know number in javascript are IEEE-754 floating point under the hood (no option about that), so not all integer value are allowed due to rounding.
For example the following code will produce this value as output -> "637889210422071800" instead of "637889210422071841" as one should expect
document.writeln(parseFloat("637889210422071841"));
document.writeln("<br>");
document.writeln(parseInt("637889210422071841",10));
Problem is that this number (basically a clock) is served in a rest api response, but when i try read the resposne i get the wrong value (probably due to some implicit deserialization over javascript object).
Luckily i have control over the rest api, so i can change how the clock is encoded.
So first option come to my mind is to change the clock format and make it a string, so that i can read the real value from javascript (i have still to interpret it, but at least the value is preserved, so this solution is a bit tricky and is not my first choiche).
Another option i think about is to decrease the size of my clock, reducing it's sensibility, basically instead of use "6378892104220718|41" i will truncate it to "6378892104220718". I prefer the second option, but i am concerned about the fact that maybe the rounding error are not uniformly distributed (due to base-exponent structure of IEEE-754 numbers).
So i would like to ask which if there is a way to handle situation like that in javascript, and if there is not a best practice, i would like to ask if reduced clock version will solve my issue.

Working with accurate currency values in Javascript

I'm working on a system that uses financial data. I'm getting subtle rounding errors due to the use of floating point numbers. I'm wondering if there's a better way to deal with this.
One of the issues is that I'm working with a mixture of different currencies, which might have up to 12 decimals, and large numbers for other currencies.
This means that the smallest number I need to represent is 0.000000000001 * (1*10^-12) and the largest 100,000,000,000 (1*10^11).
Are there any recommended ways to work with numbers of this size and not lose precision?
If you're really trying to stay in the JS realm you might consider Decimal.js which should cover your precision range.
If I were writing this and needed to make sure there were no rounding errors I would likely try and use a GMP extension for another lang inside a microservice which was only tasked with the financial math. GMPY2 for Python3 is probably a good bet for something quick and easy.

Simpler arithmetic

In Clojurescript I am doing a minus - taking one number from another:
(let [external-take-central (- external-val central-y)
_ (log "Ans: " external-take-central " from " external-val " take " central-y)
The output from this can involve quite a lot of unnecessary decimal places:
Ans: 0.10000000000000142 from 21.1 take 21
rationalize does not exist on Clojurescript, and nor does BigDecimal.
What is the best way to deal sensibly with these floating-point arithmetic errors in the Javascript execution environment?
In this case I would like external-take-central itself to not be slightly bigger/smaller than 0.1. I would like to find a generic way to make calculations accurate and easy to reason about.
It depends on how you want to use the value. You can use available javascript libraries and functions. In general, I tend ot look at the google closure library before considering loading a separate library as you avoid issues with having to define externs etc.
One possible solution would be to use the google.string lib i.e.
(ns ....
(:require [google.string :as gstr]))
(gstr/format "%.2f" value)
or something similar should work to format your value as a string with just 2 decimal places.
EDIT: Adding some more information based on comment and to clarify some points.
Note that Clojurescript numbers are just javascript numbers and can be used in any javascript function which accepts a number. So, for example you can just do
(.round js/Math 3.00001)
or even
(.toFixed 3.0002 2)
or something more complex depending on what your requirements are. I would be wary of rounding/truncating at every calculation step. There are some cases where this might make sense (for example, you might want to restrict money calculations to 2 decimal places), but in other caes, you will just increase the amount of rounding error by doing this. For thigs like delaing with currency, I would tend to use either a clojurescript or a javascript library written for that purpose.

Can javascript be trusted when making calculations?

I am implementing an invoice system, where everything is dynamically added on the dom through javascript and I am making some calculations on the browser itself with javascript.
for eg I am calculating each invoice line with quantity and price of unit and generating a total sum
price can be a floating point number
but I am not sure if this should be trusted or not, if someone has the same toughts about javascript please comment :)
I don't know but javascript doesn't seem to me to be trusted like other programming languages like PHP or so, this is my opinion, but if you can convince me please do
Thanks
Javascript uses the same data type that almost all languages use for floating point calculations. The double precision floating point data type is very common, because processors have built in support for it.
Floating point numbers have a limited precision, and most numbers with a fractional part can't be represented exactly. However, for what you are going to use it for, the precision is more than enough to show a correct result.
You should just be aware of the limited precision. When displaying the result, you should make sure that it's formatted (and rounded) to the precision that you want to show. Otherwise the limited precision might show up as for example a price of 14.9500000000000001 instead 14.95.
According to JavaScript's specifications, all numbers are 64bit precision (as in 64bit floating point precision).
From this post, you have 3 solutions:
use some implementation of Decimal for JavaScript, as BigDecimal.js
just choose a fixed number of digits to keep, like this (Math.floor(y/x) * x).toFixed(2)
switch to pure integers, treating prices as number of cents. This could lead you to big changes across the whole project
Financial calculations usually require specific fixed rules about (for example) when and how to round (in which direction), etc.
That means you'll often maintain an internal sub-total precision until you move to a next section of your calculation (like adding the tax, as per rules set).
IEEE-754 Floating point (as used in javascript) will give you a maximum accuracy of 2^53 (if you think about it like an integer).
Now your 'job' is to pretend javascript doesn't support floating point and substitute it yourself using the simplest possible way: decrease your maximum integer range to obtain the required floating point precision and see if that resulting range is suitable to your needs. If not, then you might need an external high precision math library (although most basic operations are pretty easy to implement).
First determine your desired internal precision (incl overflow digit for your expected rounding behavior): for example 3 digits:
FLOOR((2^53)/(10^3))=FLOOR(9.007.199.254.740.992/1000)=9.007.199.254.740,000
If this range is sufficient, then you need no other library, just multiply your input 10^float_digits and maintain that internal precision per calculation-section, while rounding each step according to the rules required for your calculation (you'd still need to do that when using a high-precision external math library).
For (visual) output, again, apply proper rounding and just divide your remaining value by 10^(floatDigits-roundingDigit(s)) and pass it through Number.prototype.toFixed() (which then just pads zero's when required).
As to your other question regarding trustworthiness of javascript vs other programming languages: one can even boot/run and use LINUX on javascript inside the browser: http://bellard.org/jslinux/
Let that sink in for a moment...
Now what if I told you this even works in IE6... Pretty humbling. Even servers can run on javascript (node.js)..
Hope this helps (it didn't fit in a comment).
Other answers have addressed issues that JavaScript has with using floating point numbers to represent money.
There's a separate issue with using JavaScript for calculations involving financial transactions that comes to mind.
Because the code is executed in a browser on the client machine, You can only trust the result to the extent that you can trust the client.
Therefore you should really only rely on JavaScript to calculate something that you could take for granted if the client told you.
For instance, if you were writing an e-commerce site, you could trust code that told you what the client wanted to buy, and what the clients shipping address was, but you would need to calculate the price of the goods yourself to prevent the client from telling you a lower price.
It's entirely possible that the invoicing system you're working on will only be used internally to your organisation.
If this is the case, you can disregard this entire answer.
But, if your applications is going to be used by customers to access and manipulate their invoices and orders, then this is something you'd have to consider.

Is there a definitive solution to javascript floating-point errors?

I write line of business applications. I'd like to build a front-end end using Javascript and am trying to figure out how to deal with, for a business user, are floating point errors (I understand from a computer science perspective they might not be considered errors). I've read plenty on this and seen all kinds of rounding hacks that work on examples given but seem prone to break down unexpectedly. Is there a definitive way to do decimal math in javascript?
According to Douglas Crockford, the only way around this problem is scale your values to integer. Make sure it really is an integer by using Math.round on the scaled value. (DC does not talk about the rounding part, but I discovered it was necessary. e.g. Math.round(1.1 *100)) Do calculation(s). When you are done with the math scale back to original precision. See JavaScript: The Good Parts "Floating Point" section.
One answer is to do the math in decimal instead of binary. Then you never have to worry about the decimal <=> binary conversion errors. You'd represent the numbers as binary digits in an array or a string and write the math routines yourself.
Here are some bignumber libraries you can look into if you don't want to go to that trouble:
http://jsfromhell.com/classes/bignumber
http://stz-ida.de/html/oss/js_bigdecimal.html.en
the only definite solution seems to be writing your own arbitrary precision number type working on strings internally -- which will be complicated and horribly slow.

Categories

Resources