What you do wrong is feed parseFloat()
with strings that represent decimal fractions in a human-oriented notation, whereas parseFloat()
accepts only the standard format corresponding with the JavaScript number literals, which are region-independent, always use the dot as the decimal separator, and have no thousands separator.
Futhermore, this parseFloat()
function, used in all the answers, is too generous in what it accepts as correct input, preventing the detection of what in most cases is incorrect input:
Input Result
'1Hello' 1
'1 and 2' 1
'1.2+3.4' 1.2
' 25 ' 25
In order to get a stricter and therefore better-controlled behavior, I recommend that you implement your own parsing function. Here is mine:
// Parse a decimal fraction with specified thousands
// and group separators:
function /* number */ parse_float
( /* string */ s , // string to parse
/* string */ thousep, // thousands separator, empty string if none
/* string */ decsep // decimal separator , empty string if none
)
{ var /* integer */ whole, frac ; // whole and fractinal parts
var /* integer */ wnext, fnext; // position of next char after parse
var /* integer */ fraclen ; // length of fractional part
var /* integer */ ofs ; // offset of the first digit
var /* boolean */ done ; // entire string scanned?
var /* integer */ sign ; // sign of result
var /* number */ res ; // result
/* labels */ end: { whole: {
// Check parameter types and availability:
req_param( 's' , s , 'string' );
req_param( 'thousep', thousep, 'string' );
req_param( 'decsep' , decsep , 'string' );
frac = 0;
fraclen = 0;
res = NaN;
// Account for a possible sign:
switch( s.charAt(0) )
{ case '-': sign = -1; ofs = 1; break;
case '+': sign = +1; ofs = 1; break;
default : sign = +1; ofs = 0; break;
}
[done, wnext, whole] = parse_int_ts( s, ofs, thousep );
if( isNaN( whole ) ) break end;
if( done ) break whole;
if( s.charAt( wnext ) !== decsep ) break end;
[done, fnext, frac] = parse_int( s, 0, wnext + 1 );
if( !done ) break end;
fraclen = fnext - wnext - 1;
if( fraclen === 0 ) break end;
} /* whole: */ res = ( whole + frac / Math.pow( 10, fraclen ) ) * sign;
} /* end: */ return res;
}
// Require that a value be specified and have the expected type:
function req_param( /* string */ param, /* variant */ val, /* string */ type )
{ var /* string */ errmsg;
errmsg = ''; if( val === undefined ) errmsg = 'is undefined';
else if( val === null ) errmsg = 'is null';
else if( typeof val !== type ) errmsg = `must of type \`${type}'`;
if( errmsg !== '' ) // there has been an error
{ throw new Error(`Parameter \`${param}' ${errmsg}.`); }
}
// Parse an integer with a specified thousands separator:
function /* object[] */ parse_int_ts
( /* string */ s , // input string
/* integer */ start, // start position
/* character */ sep , // thousands separator
)
// Returns an array of:
// 0: boolean -- entire string was scanned
// 1: integer -- index of next character to scan
// 2: integer -- resulting inteer
{ var /* boolean */ full;
var /* integer */ next;
var /* integer */ res;
var /* integer */ len;
var /* integer */ psep;
var /* integer */ result;
res = 0;
psep = 0;
while( true )
{ result = NaN; // mark an error
[full, next, res] = parse_int( s, res, start );
len = next - start;
if( len === 0 ) break; // nothing parsed
if( sep !== '' )
{ if( psep > 0 && len !== 3 ) break; // non-first group not 3 digits
if( psep === 0 && len > 3 ) break; // first group longer than 3 digits
}
result = res; // mark success
if( s.charAt(next) !== sep ) break;
if( full ) break;
start = next;
psep = next;
start = start + 1;
}
return [full, next, result];
}
// Parse a compact of digits beginning at position `start'
// in string `s' as an integer number:
function /* object[]*/ parse_int
( /* string */ s , // input string
/* integer */ init, // initial value
/* integer */ start // start position in `s'
)
// Returns an array of:
// 0: boolean -- entire string was scanned
// 1: integer -- index of next character to scan
// 2: integer -- result
{ const /* integer */ ASCII_0 = 48;
var /* boolean */ full; // \
var /* integer */ next; // > the return value
var /* integer */ res ; // /
var /* integer */ n, i; // string length and current position
var /* integer */ code; // character code
n = s.length;
full = true;
next = n;
res = init;
for( i = start; i < n; i += 1 )
{ code = s.charCodeAt(i);
if( code < ASCII_0 || code >= ASCII_0 + 10 )
{ next = i;
full = false;
break;
}
res = res * 10 + code - ASCII_0;
}
if( code === undefined ) res = NaN;
return [ full, next, res ];
}
And a test program that uses parse_float()
to parse numbers of your format:
function test( /* string */ s )
{ var res;
res = parse_float( s, ' ', ',' );
console.log(`${(' ' + `[${s}]`).slice(-12)} => ${res}`);
}
test( '' );
test( '12' );
test( '12a' );
test( '12,' );
test( '12,345' );
test( '12 345' );
test( '123 45' );
test( '1 234 567' );
test( '12 345 56' );
test( '12345' );
test( '12 435,678' );
test( '+1,2' );
test( '-2 345' );
It writes:
[] => NaN
[12] => 12
[12a] => NaN
[12,] => NaN
[12,345] => 12.345
[12 345] => 12345
[123 45] => NaN
[1 234 567] => 1234567
[12 345 56] => NaN
[12345] => NaN
[12 435,678] => 12435.678
[+1,2] => 1.2
[-2 345] => -2345