diff --git a/docs/docs.html b/docs/docs.html index e4fc228..271a62a 100644 --- a/docs/docs.html +++ b/docs/docs.html @@ -508,7 +508,7 @@ var csv = Papa.unparse({ dynamicTyping - If true, numeric and boolean data will be converted to their type instead of remaining strings. Numeric data must conform to the definition of a decimal literal. European-formatted numbers must have commas and dots swapped. If also accepts an object or a function. If object it's values should be a boolean to indicate if dynamic typing should be applied for each column number (or header name if using headers). If it's a function, it should return a boolean value for each field number (or name if using headers) which will be passed as first argument. + If true, numeric and boolean data will be converted to their type instead of remaining strings. Numeric data must conform to the definition of a decimal literal. Numerical values greater than 2^53 or less than -2^53 will not be converted to numbers to preserve precision. European-formatted numbers must have commas and dots swapped. If also accepts an object or a function. If object it's values should be a boolean to indicate if dynamic typing should be applied for each column number (or header name if using headers). If it's a function, it should return a boolean value for each field number (or name if using headers) which will be passed as first argument. diff --git a/papaparse.js b/papaparse.js index d6d83fd..acf4c95 100755 --- a/papaparse.js +++ b/papaparse.js @@ -996,6 +996,8 @@ License: MIT function ParserHandle(_config) { // One goal is to minimize the use of regular expressions... + var MAX_FLOAT = Math.pow(2, 53); + var MIN_FLOAT = -MAX_FLOAT; var FLOAT = /^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i; var ISO_DATE = /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/; var self = this; @@ -1123,6 +1125,16 @@ License: MIT return _config.skipEmptyLines === 'greedy' ? s.join('').trim() === '' : s.length === 1 && s[0].length === 0; } + function testFloat(s) { + if (FLOAT.test(s)) { + var floatValue = parseFloat(s); + if (floatValue > MIN_FLOAT && floatValue < MAX_FLOAT) { + return true; + } + } + return false; + } + function processResults() { if (_results && _delimiterError) @@ -1190,7 +1202,7 @@ License: MIT return true; else if (value === 'false' || value === 'FALSE') return false; - else if (FLOAT.test(value)) + else if (testFloat(value)) return parseFloat(value); else if (ISO_DATE.test(value)) return new Date(value); diff --git a/tests/test-cases.js b/tests/test-cases.js index 9d5be3a..81a2dce 100644 --- a/tests/test-cases.js +++ b/tests/test-cases.js @@ -815,11 +815,11 @@ var PARSE_TESTS = [ } }, { - description: "Dynamic typing converts numeric literals", - input: '1,2.2,1e3\r\n-4,-4.5,-4e-5\r\n-,5a,5-2', + description: "Dynamic typing converts numeric literals and maintains precision", + input: '1,2.2,1e3\r\n-4,-4.5,-4e-5\r\n-,5a,5-2\r\n16142028098527942586,9007199254740991,-9007199254740992', config: { dynamicTyping: true }, expected: { - data: [[1, 2.2, 1000], [-4, -4.5, -0.00004], ["-", "5a", "5-2"]], + data: [[1, 2.2, 1000], [-4, -4.5, -0.00004], ["-", "5a", "5-2"], ["16142028098527942586", 9007199254740991, "-9007199254740992"]], errors: [] } },