Rounding the number within two digits after the comma with javascript - javascript

I have a product to add into the store, where there are two input fields priceBase and priceFinal. First is without TAX, second is with TAX.
While using this javascript function:
jQuery(function($){
var priceBase = $('input[name="mprices[basePrice][]"]', '#productPriceBody');
var priceFinal = $('input[name="mprices[salesPrice][]"]', '#productPriceBody');
var priceDiff = priceFinal.val() - priceBase.val();
var priceTax = priceDiff / priceBase.val();
alert(priceFinal.val()); // 1.40004
alert(priceBase.val()); // 1.16667
alert(priceDiff); // 0.23333399999999993
alert(priceTax); // 0.19999999999999993
});
How I suppose to round a priceTax value from 0.19999999999999993 to 0.20 ? Like normal math calculation you know, if it's 4 and below, it rounds to lower, if it's 5 it rounds to higher number.
Thanks for suggestions in advance.

You seem to want
alert(priceDiff.toFixed(2));
But you should parse the values before you do maths. It works here because you're lucky :
"33"-"12" => "21"
"33"+"12" => "3312"
So to avoid bugs in the future (when you use + instead of - for example) I'd suggest to always parse the field values :
var priceDiff = parseFloat(priceFinal.val()) - parseFloat(priceBase.val());

Related

How to format numeric values with pattern?

I'm finding an equivalent to Java's DecimalFormat in JavaScript. I would like to format given numeric values with given decimal pattern like
"#,###.00"
"###.##"
"#,###.##"
"$#,##0.00"
"###,###.00"
"$###,###.00"
"###,###.###"
"####,####.000"
Has there any way to achieve it?
Part 1 - formatting
I would recommend using the Intl.NumberFormat natively supported in javascript, although it may not be supported by older browsers. Looks like IE11 has it, but not android.
So if you wanted to support US Dollars you would simply use something like this
var dollarFormat = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
var amountInDollars = dollarFormat.format(123456.123);
// amountInDollars = "$123,456.12"
But this also rounds up for you, for example
var roundedDollars = dollarFormat.format(555.555);
// roundedDollars = "$555.56";
For the numeric cases just use a different formatter. The default 'en-US' adds commas, a decimal before fractional numbers, and limits to 3 fractional numbers. But this is configurable.
var numberFormat = new Intl.NumberFormat('en-US');
var formatted = numberFormat.format(123456.123456);
// formatted = "123,456.123"
var numberFormat2decimals = new Intl.NumberFormat('en-US', { maximumFractionDigits: 2 });
var formatted2 = numberFormat2decimals.format(123456.123456);
// formatted2 = "123,456.12"
You can set maximum and minimum for fraction, integer, and significant digits, and this also supports international formats. Since it's native javascript, I think it's a good way to go if your platforms support it.
MDN is an excellent reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
Part 2 - the 0's
To achieve the 0's in your formats you'll have to modify the value before passing to the formatter. If you require a minimum fractional amount, you're fine for things like .00, the currency formatter will do that by default. If you've got fractal numbers you don't want, just use Math.trun() to truncate the values.
var num = Math.trun(1234.1234);
// num = 1234
Now to change something like 12345 to 12340 we'll have to remove some of the numeric value. We can find out much by converting to a string, pulling the last character, and converting back.
var num = 123456.12345;
var wholeNum = Math.trunc(num);
// wholeNum = 123456;
var toRemove = Number.parseInt(wholeNum.toString().slice(-1), 10);
// toRemove = 6
// slice(-1) gives us the right-most character of a string.
// Notice the ', 10' at the end, this is important to indicate which number base to use for parseInt.
var wholeNumEnding0 = wholeNum - toRemove;
// wholeNumEnding0 = 123450
Hopefully that's what you're looking to accomplish? I didn't perform any rounding here.
Note: I typed this at speed, excuse any mistakes, there might be better ways to do it too.
If you don't want to rely on a library, you could do something like the following:
var number = 100000.00000000000012422;
function FormatNumber(no){
no = no.toFixed(2);
no = no.toString().split('.');
var p1 = no[0];
p1 = p1.substring(0, p1.length - 3) + ',' + p1.substring(p1.length - 3);
no = p1 + '.' + no[1];
console.log(no);
}
FormatNumber(number);
The FormatNumber function takes a number as a parameter (you would probably want to expand that to include e.g. decimal places). It converts the number to the required decimal places, the turns it into a string and splits it by the decimal separator '.'.
The next step is to add a thousands separator three characters from the back, then it's just a matter of joining the remaining characters back together.
JSFiddle
If you wanted to get a ',' every 3 characters you could write a little more 'complex' formatter, something along the lines of the following:
no = no.toFixed(2);
no = no.toString().split('.');
var p1 = no[0];
var arr = [];
arr = p1.split("").reverse().join("").match(/[\s\S]{1,3}/g) || [];
arr = arr.reverse();
p1 = "";
for(var i = 0; i < arr.length; i++){
p1 += arr[i].split("").reverse().join("");
if(i != arr.length - 1){
p1 += ',';
}
}
no = p1 + '.' + no[1];
This method splits the number into an array by each number, reverses the array as we need to start from the end of the string to get accurate result.
Then we iterate the array of strings with 3 or less values by splitting the number into an array again, reversing it and joining back together and then appending to p1. If it's the last item, it doesn't add a comma.
Lastly we take the decimal and append to built string.
JSFiddle

Converting a website's price to a number with no decimals

I have a system that reads prices without decimals.
Example: 2890 = $28.90
I also have a system that takes a websites price of a product with the result being anywhere from $40.25 to just $40 (with no decimals places). I need most likely a regex or a function using javaScript or jQuery that would convert something like $40.25 to 4025 or $40 to 4000. Because I need to send the second system's returning number to the first system which will only except numbers without decimal places.
I originally thought I had it with this: item.price = Number(item.price.replace(/[^0-9\.-]+/g,"")*100); where item.price in this case equals $79.99 but I got a result back as 7998.99999999 instead of 7999 which is what I need and I can't have those decimals places, so parseFloat isn't an option. Would appreciate the help!
Don't re-invent the wheel, use a library! Try https://www.npmjs.com/package/parse-currency
import parseCurrency from 'parse-currency'
const foo = parseCurrency('$10.50')
console.log(foo) // 10.5
const bar = parseCurrency('$1,000,000.25')
console.log(bar) // 1000000.25
As Duncan mentioned, parse-currency library would be the way, but it is not enough for your problem. Let's make a better function...
function parseCurrency(amount) {
var number = amount.replace(/[^\d|\.]/g, ''); // Removes everything that's not a digit or a dot
var parsedToFloat = parseFloat(Math.round(number * 100) / 100); // Make a float number even it is an integer
return parsedToFloat.toFixed(2); // Now make sure that it will have always 2 decimal places
}
// This will return the following results...
parseCurrency('$40'); // "40.00"
parseCurrency('$40.25'); // "40.25"
parseCurrency('$40,000.25'); // "40000.25"
As you asked for a number that won't be fixed, you can do something like that:
const currencies = [
'$40',
'$45.25',
'$45.251123456789',
'$1,000',
'$1,000.25'
];
function convertToNumber(currency) {
const number = currency.replace(/[^\d|\.]/g, '');
return parseFloat(parseFloat(number).toFixed(2)) * 100;
}
console.log(currencies.map(convertToNumber))

How to simulate the result of a Google Sheet multiplication formula in Javascript?

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

Javascript Money Calculations (Inconsistent Results)

I am currently trying to calculate fees that my service charges for sales (8%). The seller can input the amount they want to receive, or want's the buyer to pay (Like Steam does here: http://i.imgur.com/pLFN9px.png). I am currently using this code:
function precise_round(num, decimals) {
var t=Math.pow(10, decimals);
return (Math.round((num * t) + (decimals>0?1:0)*(Math.sign(num) * (10 / Math.pow(100, decimals)))) / t).toFixed(decimals);
}
var elem = $(this);
elem.data('oldVal', elem.val());
elem.bind("propertychange change click keyup input paste", function(event){
if (elem.data('oldVal') != elem.val()) {
elem.data('oldVal', elem.val());
if(elem.attr("id")=="seller-gets") {
var cur = elem.val();
var value = cur*1.08;
$("#buyer-pays").val(precise_round(value), 2);
} else {
var cur = elem.val();
var value = cur*0.92;
$("#seller-gets").val(precise_round(value), 2);
}
}
});
});
The Receive(Left) <--> Pay(Right) conversions are not consistent. (Putting the same number the left produced back into the right gives a different left).
Sorry if this is not clear enough, I can explain it a little better if needed.
Those are inconsistent before you are doing two different things. Say for eg. lets take 5 as the left number.
8% of 5 = .4, So Right number will be 5.4
And if you put 5.4 as the right number 8% of it is .432 not .4. So the left number will be 5.4 - .432 = 4.968 and not 5.
If you want to get back the same number then you have to multiply by 1.08 for left -> right conversion and divide by 1.08 for the vice-versa.
Hope this helps.
Firstly, can you be sure that the values that are passed to the precise_round function are what you expect?
If not, then I think from looking at your code that you need to convert the raw values to float. I have had the same problem with values being one cent wrong, and converting to float fixed it.
var value = cur*1.08;
should be:
var value = parseFloat(cur)*1.08;
and
var value = cur*0.92;
should be:
var value = parseFloat(cur)*0.92;
After that, you can use value.toFixed(2) instead of rounding. Something to try anyway.

Tapering/Rounding decimal answers JS calculations

Using JS to run a simple formula, but like many JS calculations I've made before the decimal answers go on for way longer than I'd like and make it look sloppy.
Is there I way I can force the calculation to stop at a certain decimal place or force a round up from there?
Thanks!
<script>
var $u = $('input[name=u]');
var $a = $('input[name=a]');
var $h = $('input[name=h]');
$u.on('keyup',function() {
var u = +$u.val();
var a = +$a.val();
$h.val(u*4.605+a*1.308+28.003).toFixed(1);
});
$a.on('keyup',function() {
var u = +$u.val();
var a = +$a.val();
$h.val(u*4.605+a*1.308+28.003).toFixed(1);
});
</script>
I see you've tried toFixed(). I believe that is the solution, but you're trying to call it on a jQuery object. You should call it on the number before passing it into val().
Change your code to:
$a.on('keyup',function() {
var u = +$u.val();
var a = +$a.val();
$h.val((u*4.605+a*1.308+28.003).toFixed(1));
});
// $('#a').val(Math.PI).toFixed(1); // You're inserting the full value, and then executing
// toFixed on a jQuery Object. Not going to work.
$('#a').val( (Math.PI).toFixed(1) ); // How it should be written
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<input id="a"/>
Math.floor(number)
Returns the next integer below given number.
Math.floor(1.66); // 1
Math.floor(-1.66); // -2
Math.ceil(number)
Returns the next integer above given number.
Math.ceil(1.66); // 2
Math.ceil(-1.66); // -1
Math.round(number)
Returns the nearest integer. Similar to Math.floor(number + 0.5);
Math.round(1.66); // 2
Math.round(-1.66); // -2
number|0 (or ~~number)
Discards the decimal portion of a number.
1.66|0; // 1
-1.66|0; // -1
number.toFixed(decimals)
Converts a number into a string with the specified number of decimals. Will either pad the result with zeroes or mathematically round to a certain number of decimals.
(1.66).toFixed(1); // "1.7"
(-1.66).toFixed(1); // "-1.7"
number.toFixed(decimals + 1).slice(0, -1)
Returns a fixed-decimal string, but chops the specified number of decimals without rounding.
(1.66).toFixed(2).slice(0, -1); // "1.6"

Categories