This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is JavaScript’s Floating-Point Math Broken?
I have a strange mathematical problem during a multiplication in javascript.
$(parent).find('#id_deals-' + i + '-quantity').val()
result -> 10
$(parent).find('#id_deals-' + i + '-price').val()
result -> 3.99
Both above mulltiplied like this:
$(parent).find('#id_deals-' + i + '-price').val() * $(parent).find('#id_deals-' + i + '-quantity').val()
result --> 39.900000000000006
Why is this happening? and what can I do to limit the decimal places to 2 digits only?
Is it maybe because 10 has to be 10.0 ? But how do I convert my value to this format automatically before the actual multiplication?
Update:
According to syazdani's answer, I have tried to implement bigdecimal as suggested:
It is not well documented, but I got it working like this:
function run(opts) {
var bd = {"BigDecimal":BigDecimal, "BigInteger":BigInteger, "RoundingMode":RoundingMode};
var result;
var ops = {'*': "multiply", '/': "divide", '+': "add", '-': "subtract"};
var a = new bd.BigDecimal("" + opts.a);
var b = new bd.BigDecimal("" + opts.b);
var op = ops[opts.op];
if (op == "divide") {
return a.divide(b, 300, bd.RoundingMode.HALF_UP());
} else {
return a[op].call(a, b);
}
}
function multiply(a, b){
return run({"a":a,"b":b,"op":"*"});
}
If you are working with currency (as it seems that you are given the "price" id), you may be better served by using a so called Big Number library (such as this one: https://github.com/iriscouch/bigdecimal.js) for your math to control the math (round up vs round down, etc.). It takes a bit more work to get everything right, but it is worthwhile to avoid the Office Space math scenario.
All javascript number are IEEE-754 double precision floating points numbers. That means that they suffer from round-off errors and imprecision.
All numbers in javascript are floating point numbers, based on IEEE754.
If you want to format one as a string with a fixed number of digits after the dot, use
var formattedNumber = v.toFixed(2); // this makes a string
Related
This question already has answers here:
Why would I use Math.imul()?
(3 answers)
Is floating point math broken?
(31 answers)
Closed 10 days ago.
I need to rewrite some legacy Java code performing arithmetic transformations from Java to TypeScript/JavaScript. The problem is the legacy code uses the int Java type (signed 32-bits) and relies on overflows. I almost got what I want using Int32Array in JavaScript, but I still have a difference I can't explain. Look below.
Java:
int current = -1599751945;
int next = current * 0x08088405 + 1;
System.out.println("next = " + next);
Output: next = 374601940
Javascript:
const a = new Int32Array(4)
a[0] = -1599751945
a[1] = 0x08088405
a[2] = 1
a[3] = a[0]*a[1] + a[2]
console.log('a[3] = ' + a[3])
Output: a[3] = 374601952
Can someone explain the difference? And how can I get same result in JavaScript? I tried shift operations, coerce with |0, methods to convert etc., but best result is the one above.
Use Math.imul() in JavaScript. That should produce the correct result.
const a = new Int32Array(4)
a[0] = -1599751945
a[1] = 0x08088405
a[2] = 1
a[3] = Math.imul(a[0], a[1]) + a[2]
console.log('a[3] = ' + a[3])
Additional details as to why can be found here.
I can't provide a definitive answer as to exactly why you get these exact numbers; but consider that all numbers in JS are doubles.
So, whereas current * 0x08088405 is done using integer arithmetic in Java, a[0]*a[1] is done using double arithmetic in JS, so these intermediate results are different; and the limited precision of a double means that adding 1 to that doesn't actually change the value:
console.log(a[0]*a[1]) => -215607868985706270
console.log(a[0]*a[1] + a[2]) => = -215607868985706270
Compare this to Java, where integer arithmetic is used:
int[] a = { -1599751945, 0x08088405, 1};
System.out.println(a[0]*a[1]) => 374601939
System.out.println(a[0]*a[1] + a[2]) => 374601940
If we make Java do this in double arithmetic:
double[] a = { -1599751945, 0x08088405, 1};
System.out.println(a[0]*a[1]); => -2.15607868985706272E17
System.out.println(a[0]*a[1] + a[2]); => -2.15607868985706272E17
You can see that this is almost the same, but differs in the least significant digit:
-215607868985706270 // JS
-215607868985706272 // Java
I don't know why there is such a difference here.
Floating point numbers only support precision up to a specific number of digits. JavaScript promotes numbers larger than 32 bits to floating point numbers.
Java is "more correct" here.
// -215607868985706285 is the 64 bit result of your multiplication
console.log(-215607868985706285); // will print -215607868985706285
See Is floating point math broken? for a general discussion of this topic.
I'm trying to create a function that converts two strings to a float value.
Some external party created a theme with a backend where you should provide to values for a price:
priceBeforeComma
priceAfterComma
In the html this converts to:
<span>55,<sup>07</sup></span>
I need to do some calculations with the price as a float before splitting it up again for the html like you can see above.
I have a function that works pretty fine:
function parsePrice(price, priceDecimal) {
return parseFloat(price + "." + priceDecimal);
}
However, the problem I'm facing is that let's say I provide 07 as the decimal like above, the leading zero is removed, returning 55,7.
There is quite a big difference between 55,07 and 55,7. I expect to get back the zero as well like 55,07.
Any help will be much appreciated.
Your Code is right
function parsePrice(price, priceDecimal) {
return parseFloat(price + "." + priceDecimal);
}
parsePrice("55", "07");
if you send parsePrice("55","07") so you do not need to divide it by 100 because maybe you send it 007 then you should divide it by 1000. But your code will work properly if send string
Floats represent 07 as 07.0, so to get this to work correctly you'll need to write it as 0.07.
Here's what worked for me:
function parsePrice(price, priceDecimal) {
return parseFloat(price + priceDecimal);
}
var output = parsePrice(57, 0.07);
document.getElementById("test").innerHTML = output.toString().replace(".", ",");
<p id="test"></p>
This might be overkill, but you could use something that provides complete control over how numbers are converted to strings, and vice versa, for example: https://github.com/alexei/sprintf.js I have not used that library, but it promises to provide the same functionality as C printf, which would allow you to keep leading zeros. See the answer to the "C" language question here: Printing leading 0's in C?
(But, as an aside, also see my comment above - generally it's better to do financial calculations in integer arithmetic rather than floating point.)
So my suggestion would be to do this instead:
function price(dollars, cents) { // adjust for your currency
return parseInt(dollars)*100 + parseInt(cents);
}
function dollarsAndCents(price) {
let sign = "+";
if (price<0) {
sign = "-";
price = -price;
}
let cents = price % 100;
let dollars = (price-cents)/100;
dollars = dollars.toString();
cents = cents.toString();
if (cents.length<2) cents = "0" + cents;
return {sign: sign, dollars: dollars, cents: cents}
}
let price1 = price ("55", "07");
let price2 = price ("99", "99");
let total = price1 + price2;
console.log(dollarsAndCents(total))
//{ sign: '+', dollars: '155', cents: '06' }
let refund = -12345
console.log(dollarsAndCents(refund))
//{ sign: '-', dollars: '123', cents: '45' }
There you go, that's a pretty complete solution! Even handles negative amounts.
You should pass in Strings to your function instead by putting quotes around your numbers. Using numbers will always have the caveat of removing any leading zeroes.
function parsePrice(price, priceDecimal) {
return parseFloat(price + "." + priceDecimal);
}
parsePrice("55", "07");
Why not parse them separately as integers and add them together in the right proportions?
function parsePrice(price, priceDecimal) {
return parseInt(price) + (parseInt(priceDecimal) / 100);
}
console.log(parsePrice("55", "07"));
If the value of f5 cell in a Google Sheet is 1.1000 (a number formatted to 4 decimal places) and the value of f6 is = f5 * 1.073, how can I ensure I get the same result multiplying those values in Javascript, eg:
var original_value = 1.1000;
var derivative_value = original_value * 1.073;
Specifically, my question is - will the result of the Javascript multiplication (derivative_value) be the same as the result of the Google formula (f6)? And if not, how can I make it so that it is?
Context / What I've Tried
For context, this question is part of a larger question I am trying to resolve for which I have set up this JSFiddle.
The JSFiddle has an input for the original_value and an input for the multiplier.
It outputs the result to four decimal places and adds trailing zeros where required (this is the required format for the result).
It is an attempt to check that the Javascript code I am writing will produce the same result as the Google Sheet formula.
[ The JSFiddle has been updated to also log decimal.js results to the console for comparison ]
Edit
There was a suggestion to use decimal.js but I'm not sure how it would be applied - something like the following?
var original_value = new Decimal(1.1000);
// some different multipliers for testing
var multiplier_a = new Decimal(1.073);
var multiplier_b = new Decimal(1.1);
// some different results for testing
var derivative_value_a = original_value.times(multiplier_a).toString();
var derivative_value_b = original_value.times(multiplier_b).toString();
console.log(derivative_value_a); // 1.1803
console.log(derivative_value_b); // 1.21
Is that any more accurate than plain Javascript original_value * multiplier? More importantly for this question, will it always simulate the same result that a Google Sheet formula produces?
JavaScript is using so called double precision float format (64 bit)- https://tc39.github.io/ecma262/#sec-terms-and-definitions-number-value
Google Sheets seem to use the same format, you can test it by =f6*1E13 - round(f6*1E13) to see that f6 is not STORED as a fixed number format, only FORMATTED
see Number.toFixed how to FORMAT numbers in Javascript
to generate some test data:
[...Array(10)].forEach(() => {
const f5 = 1.1
const x = Math.random() / 100
const f6 = f5 * x
console.log(x, f6.toFixed(4))
})
and compare in Google Sheet:
https://docs.google.com/spreadsheets/d/1jKBwzM41nwIEyatLUHEUwteK8ImJg334hzJ8nKkUZ5M/view
=> all rounded numbers are equal.
P.S.: you need to copy the console output, paste into the Sheet, use the menu item Data > Split text into columns... > Space, then multiply by 1.1 in 3rd column and finally format all numbers
After revisiting this I have updated the jsFiddle.
The main components of what I believe are a satisfactory solution are:
Convert both original_value and multiplier to decimal.js objects.
Do the multiplication using the decimal.js times method.
Do the rounding using the decimal.js toDecimalPlaces method.
Use the argument values (4,7) to define 4 decimal places with ROUND_HALF_CEIL rounding, equivalent to Math.round (reference)
For example:
var my_decimal_js_value = new Decimal(original_value).times(new Decimal(multiplier)).toDecimalPlaces(4, 7);
In order to add any necessary trailing zeros to the result, I use:
function trailingZeros(my_decimal_js_value) {
var result = my_decimal_js_value;
// add zeros if required:
var split_result = result.toString().split(".");
// if there are decimals present
if (split_result[1] != undefined) {
// declare trailing_zeros;
var trailing_zeros;
// get the amount of decimal numbers
decimals_present = split_result[1].length;
// if one decimal number, add three trailing zeros
if (decimals_present === 1) {
trailing_zeros = "000";
result += trailing_zeros;
}
// if two decimal numbers, add two trailing zeros
else if (decimals_present === 2) {
trailing_zeros = "00";
result += trailing_zeros;
}
// if three decimal numbers, add one trailing zero
else if (decimals_present === 3) {
trailing_zeros = "0";
result += trailing_zeros;
}
// if four decimal numbers, just convert result to string
else if (decimals_present === 4) {
result = result.toString();
}
}
// if there are no decimals present, add a decimal place and four zeros
else if (split_result[1] === undefined) {
trailing_zeros = ".0000";
result += trailing_zeros;
}
return result;
}
I am still not absolutely certain that this mimics the Google Sheet multiplication formula, however using decimal.js, or another dedicated decimal library, seems to be the preferred method over plain JavaScript (to avoid possible rounding errors), based on posts such as these:
http://www.jacklmoore.com/notes/rounding-in-javascript
Is floating point math broken?
https://spin.atomicobject.com/2016/01/04/javascript-math-precision-decimals
This question already has answers here:
How to deal with floating point number precision in JavaScript?
(47 answers)
Closed 9 years ago.
alert(5.30/0.1);
This gives 52.99999999999999 but should be 53. Can anybody tell how and why?
I want to find that a number is divisible by a given number. Note that one of the number may be a float.
For the same reason that
0.1 * 0.2 //0.020000000000000004
Some decimal numbers can't be represented in IEEE 754, the mathematical representation used by JavaScript. If you want to perform arithmetic with these numbers in your question, it would be better to multiply them until they are whole numbers first, and then divide them.
Scale the numbers to become whole. Then modulus the result.
alert((5.30*10) % (0.1*10));
Now that you have read the article i commented, you should know the root of your problem.
You can partially work around that by scaling you floats...
Then just write a function which:
If its a float
Scale the Numbers
return a boolean representation of the divisibility of the number
function isDivisable(n, d) {
var ndI = 1 + "".indexOf.call(n, "."); //Index of the Number's Dot
var ddI = 1 + "".indexOf.call(d, "."); // Index of the Divisors Dot
if (ndI || ddI) { // IF its a float
var l = Math.max(("" + n).length - ndI, ("" + d).length - ddI); //Longest Decimal Part
var tmpN = (n * Math.pow(10, l)); //scale the float
var tmpD = (d * Math.pow(10, l));
return !~((tmpN % tmpD) - 1); //Substract one of the modulo result, apply a bitwise NOT and cast a boolean.
}
return !~((n % d) - 1); // If it isnt a decimal, return the result
}
console.log(isDivisable(5.30, 0.1));//true
Heres a JSBin
However...
As Integers are stored with 64bit precision, the maximum precision lies about (2^53),
and you will soon exceed the maximum precision when scaling larger numbers.
So it might be a good idea to get some sort of BigInteger Library for javascript
if you want to test floats for divisibility
To find if a number x is divisible by a number y you have to do x % y (modulo). If the result is 0, it is perfectly divisible, any other isn't.
You can get it by following:
var num = (5.30/0.1);
alert(num.toFixed(2));
this will give you 53.00.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is JavaScript's Math broken?
I'm attempting to add up three input fields, each containing a value of 33.3 which should total 99.9, however they are totaling to 99.89999999999999
Could someone explain how this is happening. Below is my code. Thanks in advance.
$("#modify-funding input.percentCalc").sumValues()
$.fn.sumValues = function () {
var sum = 0;
this.each(function () {
sum += $(this).fieldVal();
});
return sum;
};
$.fn.fieldVal = function () {
var val;
if ($(this).is(':input')) {
val = $(this).val();
alert("val " + val);
} else {
val = $(this).text();
}
return parseFloat(('0' + val).replace(/[^0-9-\.]/g, ''), 10);
};
Welcome to the wonderful world of floating point numbers. Floating points are aproximations of the number you want to represent. Thus when you save a number as 33.3 it is around but not exactly 33.3 this error adds up after multiple operations. The best way to compare floats is to not test for equality but to test weather they are in a range.
Instead of
if(x == 99.9)
try
if(Math.abs(99.9 - x) < .1)
If you just want the string representation. You could try handing the floating point number as an integer. i.e. 33.3 equals 333 then when you are turning it back into a string you add the decimal back in where appropriate. This would be the best solution for your problem.