This is "By Design". The parseFloat function will only consider the parts of the string up until in reaches a non +, -, number, exponent or decimal point. Once it sees the comma it stops looking and only considers the "75" portion.
- https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseFloat
To fix this convert the commas to decimal points.
var fullcost = parseFloat($("#fullcost").text().replace(',', '.'));
Answer from JaredPar on Stack OverflowThis is "By Design". The parseFloat function will only consider the parts of the string up until in reaches a non +, -, number, exponent or decimal point. Once it sees the comma it stops looking and only considers the "75" portion.
- https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseFloat
To fix this convert the commas to decimal points.
var fullcost = parseFloat($("#fullcost").text().replace(',', '.'));
javascript's parseFloat doesn't take a locale parameter. So you will have to replace , with .
parseFloat('0,04'.replace(/,/, '.')); // 0.04
How can I add a thousand separator to a parseFloat value?
For example - if the value is 96645 I would like to show as 96,645.
Thanks for any help
Numbers parsed incorrectly if "." is thousands separator, "," decimal separator and decimal part not specified
Globalize.parseFloat does not consistently parse group and decimal separators
Function to format numbers with thousand separators and decimals
Thousands separator in jQuery is causing parseFloat to misinterpret numbers
Yes remove the commas:
let output = parseFloat("2,299.00".replace(/,/g, ''));
console.log(output);
Removing commas is potentially dangerous because, as others have mentioned in the comments, many locales use a comma to mean something different (like a decimal place).
I don't know where you got your string from, but in some places in the world "2,299.00" = 2.299
The Intl object could have been a nice way to tackle this problem, but somehow they managed to ship the spec with only a Intl.NumberFormat.format() API and no parse counterpart :(
The only way to parse a string with cultural numeric characters in it to a machine recognisable number in any i18n sane way is to use a library that leverages CLDR data to cover off all possible ways of formatting number strings http://cldr.unicode.org/
The two best JS options I've come across for this so far:
- https://github.com/google/closure-library/tree/master/closure/goog/i18n
- https://github.com/globalizejs/globalize
You can use default parameters to automatically assign a value if none is given.
function neat(numToFormat, thousandSep = ".", decimalSep = ",")
You should start by checking if the input is valid, instead of doing it at the end. It would also be better to throw an error, since it's not easy to tell from the result if the formatting succeeded or not.
if (typeof numToFormat == 'number') {
if(Number.isNaN(numToFormat) || !Number.isFinite(numToFormat)) {
throw new Error("Invalid number");
}
}
else if(typeof numToFormat == 'string') {
if(!/^\d+((,|\.)\d+)?$/.test(numToFormat)) {
throw new Error("Invalid string");
}
}
else {
throw new Error("Can only use number or string");
}
It's not parseFloat() which is imprecise, but the numbers themselves. JavaScript uses IEEE 754, which is 64-bit floating point numbers, with 53 bits used for precision. The number you are testing against is the highest safe integer, which is 2^53-1 and can be gotten from Number.MAX_SAFE_INTEGER. But your result could still be imprecise with numbers this large, since you are not only using integers but also 2 decimal places.
With string inputs you can get around this by not converting it to a number at all. Since the result is also a string, you can keep it a string all the way through. For number inputs you can only work with the precision you've been given. Any imprecision should be handled by the code calling this function beforehand.
You will need to update your parseInput function to avoid parseFloat. Just cut of any extra decimals, or add them if they are missing. If you want to round the number it gets a bit harder, but it's still possible.
Your placeThousands function can be done a little simpler. There are several ways to do it, but I would cut the string into an array of chunks and then join them together. I would also make it easier to change the chunk size by putting it in a variable.
const chunkSize = 3;
let chunks = [];
let start = wholesPart.length % chunkSize;
if(start != 0) {
chunks.push(wholesPart.slice(0, start));
}
for(let i = start; i < wholesPart.length; i+= chunkSize) {
chunks.push(wholesPart.slice(i, i + chunkSize));
}
return chunks.join(thousandSep)
When I saw fstChunk the first thing I thought was 'fast chunk', but I guess it's supposed to be 'first chunk'. There is no reason make things less clear just to save 2 characters.
You don't need to check the length of wholesPart, placeThousands can handle short strings.
return placeThousands(wholesPart) + decimalPart;
Style and code
For max integer value use
Number.MAX_SAFE_INTEGERUse
===rather than==and!==rather than!=typeofis not a function.typeof(numToFormat) == 'number';can be writtentypeof numToFormat === "number";Use
isNaN(number)to determine if a variable is "Not a Number"Use
Number(val)to convert to a number rather thanparseFloat(val)If you are going to exit on
NaNor out of range do it early rather than after you process everything.If a variable is not going to change define it as a constant with
constneatis a rather ambiguous name for the function. MaybeformatNumberwould be betterSpaces between operators make the code more readable.
nextLoop = i+3;asnextLoop = i + 3;Space between
ifand(alsofor (,while (and other tokens followed by(. And space between) {
Logic
The whole thing feels over complicated. For values under 1000 all you do is maybe replace the decimal point. For other values you need only handle the thousands separator.
There is also the issues of negative numbers. You don't return a string saying No can do. The value you return will have a 1000 separator in the wrong place if the number length is divisible by 3 eg neat(-100) returns "-.100,00"
Rewrite
The rewrite fixes the negative number problem.
I added a places variable so that the number of decimal places can be set.
Uses default parameters to define defaults for (new
placesarg),thousandSepanddecimalSepRather than return error string I return the number argument. It is likely that if the value overflows or is not a number the calling code will not check if the result is one of the error strings. This way what goes in will still have meaning when it comes out.
Code
function formatNumber(number, thousandSep = ",", decimalSep = ".", places = 2) {
if (isNaN(number)) { return number }
var result = number < 0 ? "-" : "";
number = Math.abs(number);
if (number >= Number.MAX_SAFE_INTEGER) { return result + number.toFixed(places) }
var place = Math.ceil(Math.log10(number));
if (place < 3) {
return result + number.toFixed(places).replace(".", decimalSep);
}
while (place--) {
result += number / 10 ** place % 10 | 0;
if (place > 0 && place % 3 === 0) { result += thousandSep }
}
return result + decimalSep + number.toFixed(places).split(".")[1];
}
parseFloat( theString.replace(/,/g,'') );
I don't know why no one has suggested this expression-
parseFloat( theString.replace(/[^\d\.]/g,'') );
Removes any non-numeric characters except for periods. You don't need custom functions/loops for this either, that's just overkill.
According to the specification, a DecimalLiteral is defined as:
DecimalLiteral ::
DecimalIntegerLiteral . DecimalDigitsopt ExponentPartopt
. DecimalDigits ExponentPartopt
DecimalIntegerLiteral ExponentPartopt
and for satisfying the parseFloat argument:
- Let inputString be ToString(string).
- Let trimmedString be a substring of inputString consisting of the leftmost character that is not a StrWhiteSpaceChar and all characters to the right of that character.(In other words, remove leading white space.)
- If neither trimmedString nor any prefix of trimmedString satisfies the syntax of a StrDecimalLiteral (see 9.3.1), return NaN.
- Let numberString be the longest prefix of trimmedString, which might be trimmedString itself, that satisfies the syntax of a StrDecimalLiteral.
- Return the Number value for the MV
So numberString becomes the longest prefix of trimmedString that satisfies the syntax of a StrDecimalLiteral, meaning the first parseable literal string number it finds in the input. Only the . can be used to specify a floating-point number. If you're accepting inputs from different locales, use a string replace:
function parseLocalNum(num) {
return +(num.replace(",", "."));
}
The function uses the unary operator instead of parseFloat because it seems to me that you want to be strict about the input. parseFloat("1ABC") would be 1, whereas using the unary operator +"1ABC" returns NaN. This makes it MUCH easier to validate the input. Using parseFloat is just guessing that the input is in the correct format.
use:
theNumber.toLocaleString();
to get a properly formatted string with the right decimal and thousands separators
The reference cited in the original answer below was wrong. There is a built in function for this, which is exactly what kaiser suggests below: toLocaleString
So you can do:
(1234567.89).toLocaleString('en') // for numeric input
parseFloat("1234567.89").toLocaleString('en') // for string input
The function implemented below works, too, but simply isn't necessary.
(I thought perhaps I'd get lucky and find out that it was necessary back in 2010, but no. According to this more reliable reference, toLocaleString has been part of the standard since ECMAScript 3rd Edition [1999], which I believe means it would have been supported as far back as IE 5.5.)
Original Answer
According to this reference there isn't a built in function for adding commas to a number. But that page includes an example of how to code it yourself:
function addCommas(nStr) {
nStr += '';
var x = nStr.split('.');
var x1 = x[0];
var x2 = x.length > 1 ? '.' + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + ',' + '$2');
}
return x1 + x2;
}
Edit: To go the other way (convert string with commas to number), you could do something like this:
parseFloat("1,234,567.89".replace(/,/g,''))
If is about localizing thousands separators, delimiters and decimal separators, go with the following:
// --> numObj.toLocaleString( [locales [, options] ] )
parseInt( number ).toLocaleString();
There are several options you can use (and even locales with fallbacks):
number = 123456.7089;
result = parseInt( number ).toLocaleString() + "<br>";
result += number.toLocaleString( 'de-DE' ) + "<br>";
result += number.toLocaleString( 'ar-EG' ) + "<br>";
result += number.toLocaleString( 'ja-JP', {
style : 'currency',
currency : 'JPY',
currencyDisplay : 'symbol',
useGrouping : true
} ) + "<br>";
result += number.toLocaleString( [ 'jav', 'en' ], {
localeMatcher : 'lookup',
style : 'decimal',
minimumIntegerDigits : 2,
minimumFractionDigits : 2,
maximumFractionDigits : 3,
minimumSignificantDigits : 2,
maximumSignificantDigits : 3
} ) + "<br>";
var el = document.getElementById( 'result' );
el.innerHTML = result;
<div id="result"></div>
Details on the MDN info page.
Edit: Commentor @I like Serena adds the following:
To support browsers with a non-English locale where we still want English formatting, use
value.toLocaleString('en'). Also works for floating point.
Do a replace first:
parseFloat(str.replace(',','.').replace(' ',''))
I realise I'm late to the party, but I wanted a solution for this that properly handled digit grouping as well as different decimal separators for currencies. As none of these fully covered my use case I wrote my own solution which may be useful to others:
function parsePotentiallyGroupedFloat(stringValue) {
stringValue = stringValue.trim();
var result = stringValue.replace(/[^0-9]/g, '');
if (/[,\.]\d{2}$/.test(stringValue)) {
result = result.replace(/(\d{2})$/, '.$1');
}
return parseFloat(result);
}
This should strip out any non-digits and then check whether there was a decimal point (or comma) followed by two digits and insert the decimal point if needed.
It's worth noting that I aimed this specifically for currency and as such it assumes either no decimal places or exactly two. It's pretty hard to be sure about whether the first potential decimal point encountered is a decimal point or a digit grouping character (e.g., 1.542 could be 1542) unless you know the specifics of the current locale, but it should be easy enough to tailor this to your specific use case by changing \d{2}$ to something that will appropriately match what you expect to be after the decimal point.
function seprator(input) {
let nums = input.value.replace(/,/g, '');
if (!nums || nums.endsWith('.')) return;
input.value = parseFloat(nums).toLocaleString();
}
<input type="text" id="inputSep" oninput="seprator(this)" placeholder="123,456,789"/>
codepen example link
Consider letting the browser do the work for you with Intl.NumberFormat
const $input = document.querySelector('input');
const $output = document.querySelector('div');
$input.addEventListener('input', (evt) => {
$output.innerText = Intl.NumberFormat().format(evt.target.value);
});
<input>
<div></div>
You can customize many things about the format of the outputted number, and in your case you might like to tweak the minimumFractionDigits or maximumFractionDigits settings:
const $input = document.querySelector('input');
const $output = document.querySelector('div');
$input.addEventListener('input', (evt) => {
$output.innerText = Intl.NumberFormat(undefined, {
maximumFractionDigits: 2
}).format(evt.target.value);
})
<input>
<div></div>