How to ensure integer division gives out correct results? - javascript

I am aware of the fact that floating point calculation is likely to be inaccurate. For example:
var x = 100*0.333;
Here x gets set to 33.300000000000004 rather than 33. This might seem to be a minor problem, but it becomes serious if rounding is involved. For example, Math.ceil(x) will be incorrect - will yield 34 rather than 33.
The problem is that I was told that JS doesn't make much difference between integers and floating points. So I'm becoming worried if there's any way to ensure that integral division is correct.
The usual formula for integer division in JS seems to be Math.floor(n/m), where n, m are integers. So now let's say that m divides n evenly. Since JS treats every number as if it was a floating point number, n/m will return a floating point number. If this floating point number is not exact, but is even a very little bit smaller than the actual calculation result, then Math.floor(n/m) will get rounded down to one integer below it should.
To illustrate: if, for example, 9/3 yields 2.999999999999999 rather than 3, then Math.floor(9/3) will incorrectly yield 2 rather than 3. Well, actually, Math.floor(9/3) does give out correct results, but this is of course only an example; substitute 9 and 3 with any unfortunate choice of integers.
Can such scenario ever happen? If so, is there any way to ensure correct results?

Can such scenario ever happen?
No. If they are integers (i.e. floating numbers with no fractional part) and evenly divide, then the result will be exact and an integer again.
If the don't evenly divide, the result will always be greater than the exact result from integer division. You'll never get a 2.9999999… number that was supposed to be a 3. The floating point results are as precise as representable, rounding does not destroy the total preorder.

if you want an exact integral division result. Then do this
Math.round((m - m%n)/n)

If the value of dividing two numbers is supposed to be 3 then it will be. If the value is supposed to be 2.999999999999, it will be. Java doesn't do some kind of strange hidden rounding, up or down. If you ALWAYS need to have the value round down then Use Floor(x/y) Even if the value is 2.99999999999999999999999999999999999 it will still come back as 2. That's what Floor does. Same thing with celling. if the value is 3.0000000000000000000001 then it will be set to 4. I teach this in college, this is how it's supposed to and does work.

Related

Rounding up or down when 0.5

I am having an issue with the way Javascript is rounding numbers when hitting 0.5.
I am writing levies calculators, and am noticing a 0.1c discrepancy in the results.
The problem is that the result for them is 21480.705 which my application translates into 21480.71, whereas the tariff says 21480.70.
This is what I am seeing with Javascript:
(21480.105).toFixed(2)
"21480.10"
(21480.205).toFixed(2)
"21480.21"
(21480.305).toFixed(2)
"21480.31"
(21480.405).toFixed(2)
"21480.40"
(21480.505).toFixed(2)
"21480.51"
(21480.605).toFixed(2)
"21480.60"
(21480.705).toFixed(2)
"21480.71"
(21480.805).toFixed(2)
"21480.81"
(21480.905).toFixed(2)
"21480.90"
Questions:
What the hell is going on with this erratic rouding?
What's the quickest easiest way to get a "rounded up" result (when hitting 0.5)?
So as some of the others already explained the reason for the 'erratic' rounding is a floating point precision problem. You can investigate this by using the toExponential() method of a JavaScript number.
(21480.905).toExponential(20)
#>"2.14809049999999988358e+4"
(21480.805).toExponential(20)
#>"2.14808050000000002910e+4"
As you can see here 21480.905, gets a double representation that is slightly smaller than 21480.905, while 21480.805 gets a double representation slightly larger than the original value. Since the toFixed() method works with the double representation and has no idea of your original intended value, it does all it can and should do with the information it has.
One way to work around this, is to shift the decimal point to the number of decimals you require by multiplication, then use the standard Math.round(), then shift the decimal point back again, either by division or multiplication by the inverse. Then finally we call toFixed() method to make sure the output value gets correctly zero-padded.
var x1 = 21480.905;
var x2 = -21480.705;
function round_up(x,nd)
{
var rup=Math.pow(10,nd);
var rdwn=Math.pow(10,-nd); // Or you can just use 1/rup
return (Math.round(x*rup)*rdwn).toFixed(nd)
}
function round_down(x,nd)
{
var rup=Math.pow(10,nd);
var rdwn=Math.pow(10,-nd);
return (Math.round(x*-rup)*-rdwn).toFixed(nd)
}
function round_tozero(x,nd)
{
return x>0?round_down(x,nd):round_up(x,nd)
}
console.log(x1,'up',round_up(x1,2));
console.log(x1,'down',round_down(x1,2));
console.log(x1,'to0',round_tozero(x1,2));
console.log(x2,'up',round_up(x2,2));
console.log(x2,'down',round_down(x2,2));
console.log(x2,'to0',round_tozero(x2,2));
Finally:
Encountering a problem like this is usually a good time to sit down and have a long think about wether you are actually using the correct data type for your problem. Since floating point errors can accumulate with iterative calculation, and since people are sometimes strangely sensitive with regards to money magically disappearing/appearing in the CPU, maybe you would be better off keeping monetary counters in integer 'cents' (or some other well thought out structure) rather than floating point 'dollar'.
The why -
You may have heard that in some languages, such as JavaScript, numbers with a fractional part are calling floating-point numbers, and floating-point numbers are about dealing with approximations of numeric operations. Not exact calculations, approximations. Because how exactly would you expect to compute and store 1/3 or square root of 2, with exact calculations?
If you had not, then now you've heard of it.
That means that when you type in the number literal 21480.105, the actual value that ends up stored in computer memory is not actually 21480.105, but an approximation of it. The value closest to 21480.105 that can be represented as a floating-point number.
And since this value is not exactly 21480.105, that means it is either slightly more than that, or slightly less than that. More will be rounded up, and less will be rounded down, as expected.
The solution -
Your problem comes from approximations, that it seems you cannot afford. The solution is to work with exact numbers, not approximate.
Use whole numbers. Those are exact. Add in a fractional dot when you convert your numbers to string.
This works in most cases. (See note below.)
The rounding problem can be avoided by using numbers represented in
exponential notation:
function round(value, decimals) {
return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}
console.log(round(21480.105, 2).toFixed(2));
Found at http://www.jacklmoore.com/notes/rounding-in-javascript/
NOTE: As pointed out by Mark Dickinson, this is not a general solution because it returns NaN in certain cases, such as round(0.0000001, 2) and with large inputs.
Edits to make this more robust are welcome.
You could round to an Integer, then shift in a comma while displaying:
function round(n, digits = 2) {
// rounding to an integer is accurate in more cases, shift left by "digits" to get the number of digits behind the comma
const str = "" + Math.round(n * 10 ** digits);
return str
.padStart(digits + 1, "0") // ensure there are enough digits, 0 -> 000 -> 0.00
.slice(0, -digits) + "." + str.slice(-digits); // add a comma at "digits" counted from the end
}
What the hell is going on with this erratic rouding?
Please reference the cautionary Mozilla Doc, which identifies the cause for these discrepancies. "Floating point numbers cannot represent all decimals precisely in binary which can lead to unexpected results..."
Also, please reference Is floating point math broken? (Thank you Robby Cornelissen for the reference)
What's the quickest easiest way to get a "rounded up" result (when hitting 0.5)?
Use a JS library like accounting.js to round, format, and present currency.
For example...
function roundToNearestCent(rawValue) {
return accounting.toFixed(rawValue, 2);
}
const roundedValue = roundToNearestCent(21480.105);
console.log(roundedValue);
<script src="https://combinatronics.com/openexchangerates/accounting.js/master/accounting.js"></script>
Also, consider checking out BigDecimal in JavaScript.
Hope that helps!

Math inaccuracy in Javascript: safe to use JS for important stuff?

I was bored, so I started fidlling around in the console, and stumbled onto this (ignore the syntax error):
Some variable "test" has a value, which I multiply by 10K, it suddenly changes into different number (you could call it a rounding error, but that depends on how much accuracy you need). I then multiply that number by 10, and it changes back/again.
That raises a few questions for me:
How in accurate is Javascript? Has this been determined? I.e. a number that can be taken into account?
Is there a way to fix this? I.e. to do math in Javascript with complete accuracy (within the limitations of its datatype).
Should the changed number after the second operation be interpreted as 'changing back to the original number' or 'changing again, because of the inaccuracy'?
I'm not sure whether this should be a separate question, but I was actually trying to round numbers to a certain amount after the decimal point. I've researched it a bit, and have found two methods:
> Method A
function roundNumber(number, digits) {
var multiple = Math.pow(10, digits);
return Math.floor(number * multiple) / multiple;
}
> Method B
function roundNumber(number, digits) {
return Number(number.toFixed(digits));
}
Intuitively I like method B more (looks more efficient), but I don't know what going on behind the scenes so I can't really judge. Anyone have an idea on that? Or a way to benchmark this? And why is there no native round_to_this_many_decimals function? (one that returns an integer, not a string)
How in accurate is Javascript?
Javascript uses standard double precision floating point numbers, so the precision limitations are the same as for any other language that uses them, which is most languages. It's the native format used by the processor to handle floating point numbers.
Is there a way to fix this? I.e. to do math in Javascript with complete accuracy (within the limitations of its datatype).
No. The precision limitations lies in the way that the number is stored. Floating point numbers doesn't have complete accuracy, so no matter how you do the calculations you can't achieve absolute accuracy as the result goes back into a floating point number.
If you want complete accuracy then you need to use a different data type.
Should the changed number after the second operation be interpreted as
'changing back to the original number' or 'changing again, because of
the inaccuracy'?
It's changing again.
When a number is converted to text to be displayed, it's rounded to a certain number of digits. The numbers that look like they are exact aren't, it's just that the limitations in precision doesn't show up.
When the number "changes back" it's just because the rounding again hides the limitations in the precision. Each calculation adds or subtracts a small inaccuracy in the number, and sometimes it just happens to take the number closer to the number that you had originally. Eventhough it looks like it's more accurate, it's actually less accurate as each calculation adds a bit of uncertainty.
Internally, JavaScript uses 64-bit IEEE 754 floating-point numbers, which are a widely used standard and usually guarantee about 16 digits of accuracy. The error you witnessesed was on the 17th significant digit of the number and was reeeally tiny.
Is there a way to [...] do math in Javascript with complete accuracy (within the limitations of its datatype).
I would say that JavaScript's math is completely accurate within the limitations of its datatype. The error you witnessed was outside of those limitations.
Are you working with calculations that require a higher degree of precision than that?
Should the changed number after the second operation be interpreted as 'changing back to the original number' or 'changing again, because of the inaccuracy'?
The number never really became more or less accurate than the original value. It was only when the value was converted into a decimal value that a rounding error became apparent. But this was not a case of the value "changing back" to an accurate number. The rounding error was just too small to display.
And why is there no native round_to_this_many_decimals function? (one that returns an integer, not a string)
"Why is the language this way" questions are not considered very productive here, but it is easy to get around this limitation (assuming you mean numbers and not integers). This answer has 337 upvotes: +numb.toFixed(digits);, but note that if you try to display a number produced with that expression, there's no guarantee that it will actually display with only six digits. That's probably one of the reasons why JavaScript's "round to N places" function produces a string and not a number.
I came across the same few times and with further research I was able solve the little issues by using the library below
Math.js Library
Sample
import {
atan2, chain, derivative, e, evaluate, log, pi, pow, round, sqrt
} from 'mathjs'
// functions and constants
round(e, 3) // 2.718
atan2(3, -3) / pi // 0.75
log(10000, 10) // 4
sqrt(-4) // 2i
pow([[-1, 2], [3, 1]], 2) // [[7, 0], [0, 7]]
derivative('x^2 + x', 'x') // 2 * x + 1
// expressions
evaluate('12 / (2.3 + 0.7)') // 4
evaluate('12.7 cm to inch') // 5 inch
evaluate('sin(45 deg) ^ 2') // 0.5
evaluate('9 / 3 + 2i') // 3 + 2i
evaluate('det([-1, 2; 3, 1])') // -7
// chaining
chain(3)
.add(4)
.multiply(2)
.done() // 14

Summing numbers javascript [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 9 years ago.
'-15.48' - '43'
Just wrote this in console and result is the following:
-58.480000000000004
Why Is it so? And what to do to get correct result?
Because all floating point math is like this and is based on the IEEE 754 standard. JavaScript uses 64-bit floating point representation, which is the same as Java's double.
to fix it you may try:
(-15.48 - 43).toFixed(2);
Fiddle demo
use: toFixed()
var num = 5.56789;
var n=num.toFixed(2);
result:5.57
http://en.wikipedia.org/wiki/Machine_epsilon
Humans count in decimal numbers, machines mostly use binary. 10 == 2x5; 2 and 5 are mutually prime numbers. That trivial fact has an unpleasant consequence though.
In most calculations (except lucky degenerate cases) with "simple" decimal numbers, that include division, the result would be an indefinite repeating binary number.
In most calculations (except lucky degenerate cases) with "simple" binary numbers, that include division, the result would be an indefinite repeating decimal number.
One can check this using pen and pencil as described http://en.wikipedia.org/wiki/Repeating_decimal#Every_rational_number_is_either_a_terminating_or_repeating_decimal
That means that almost every time you see the the result of floating point calculations on your screen - as a finite number - the computer somewhat cheats at you and shows you some approximation instead of the real result.
That means that almost every time you store the results of the calculation in the variable - which has a finite size, not any larger than the computer's available memory - the computer somewhat cheats at you and retains some approximation instead of the real result.
The typical gotchas then may include.
Program is accounting for the sum of some looong sequence, an infinite loop of x AVG := AVG + X[i]; kind where 0 < X[i] < const. If the loop would run long enough, you would see that at some point AVG no more changes the value, all the elements fro mthatpoint further on are just discarded.
Program calculates some value twice using different formulas and then makes a safety check like Value_1 == Value_2. For theoretical mathematics the values are equal, for the real computers they are not.

Strange math with rounding and multiplying

I have an odd problem. I'm grabbing numbers from the web audio API, what I get out is a constant list of:
3.1051260268969075e-28 etc etc
I want to round that figure, but using:
Math.round(magAmount);
Always returns 0. magAmount is the variable I store the number in. Another odd thing, if I multiply the number by 5 I get a value lower than the original, in this case 1.5525630134484537e-27.
Any ideas?
Thanks.
That number is 5 times bigger.
e-28 is smaller than e-27
Note that e-28 is *10^-28, so there's nothing strange in your code.
And since this number is tiny, rounding it will certainly returns a 0.
Math.round() will round to the next integer, so rounding 0.0000000000..00001 (which you are doing) will round to 0.
also e-27 is bigger than e-28, so your multiply is correct
Round to what precision? If you want
3.1051260268969075e-28 -> 3.1e-28 then
Math.round(3.1051260268969075e-28 * 1.0e+29) * 1.0e-29

Better way of rounding off or rounding up in javascript

I have this problem with rounding up or rounding off with javascript. Well it gets the job done but there are some scenarios that it fails to do the job properly.
For example.
The price is 0.30 with a 15% discount should have 0.255 or 0.26 as result. (this is somehow okay)
The price is 79.99 with a 50% discount should have 40 as result. (actual result is 39.995)
Tried using this as reference Using toFixed(2) and math round to get correct rounding
also did this process Math.round(value*Math.pow(10,dec))/Math.pow(10,dec);
Any ideas?
Thanks
This is my initial test. Though this is just an initial test what I did is checked if the last two digits is greater than 5 then apply Math.ceil that would to the 0.255 to be 0.26 but when using 39.99 the result would still be 39.99 though 39.99 should use Math.round so that the result would be 40.
What I would like to accomplish is to be able to round up or round down any number if it needs rounding up or if it needs rounding up.
UPDATE
I was able to solve my own problem. (Atleast to my specific environment)
http://jsfiddle.net/xMj3M/4/
This is because of how floats are handled. I would suggest having your original numbers in integers only, so you would start with 7999. If you use this method, you will get the correct result.
In general, especially when dealing with money, it is always a good idea to work in pennies first, and then output it in the format of the currency.
You could try:
var someNumber = 36.7327;
var someNumberRoundedTo2Decimals = (Math.round((someNumber * 100)) / 100);
// = 36.73
basically this moves the decimal to the right 2 digits..
3673.27
then rounds it off to an int
3673
then moves the decimal back to the left 2 places
36.73

Categories