*
* @param {string} str A string representing a number expressed using scientific notation (e.g., `"1E+18"`).
* @param {boolean} truncate If `true`, decimals will be truncated in the expanded string. Defaults to `false`.
- * @returns {string|null} A string representing the normal form of the number (e.g., `"1000000000000000000"`), or `null`
- * if the input could not be parsed.
+ * @returns {string} A string representing the normal form of the number (e.g., `"1000000000000000000"`).
+ * @throws {TypeError} If `str` is not a `string`, a `TypeError` will be thrown.
+ * @throws {SyntaxError} If `str` is not valid scientific notation, a `SyntaxError` will be thrown.
*/
export function expandScientificNotation(str, truncate = false) {
+ if (typeof str !== 'string') {
+ throw new TypeError(`Expected a string, but received ${typeof str}.`);
+ }
+ const [mantissa, exponent] = str.split('E');
+ if (mantissa === undefined || exponent === undefined) {
+ throw new SyntaxError(`Unable to parse string "${str}" as scientific notation.`);
+ }
+ const e = Number(exponent);
let expanded = null;
- const [mantissa, exponent] = str.split?.('E') ?? [undefined, undefined];
- if (mantissa !== undefined && exponent !== undefined) {
- const e = Number(exponent);
- if (mantissa.indexOf(DECIMAL_SEPARATOR) === -1) {
- expanded = mantissa + '0'.repeat(e);
+ if (mantissa.indexOf(DECIMAL_SEPARATOR) === -1) {
+ expanded = mantissa + '0'.repeat(e);
+ }
+ else {
+ const [integer, decimal] = mantissa.split('.');
+ expanded = integer + decimal.padEnd(e, '0');
+ if (truncate) {
+ expanded = expanded.substring(0, e + 1);
}
- else {
- const [integer, decimal] = mantissa.split('.');
- expanded = integer + decimal.padEnd(e, '0');
- if (truncate) {
- expanded = expanded.substring(0, e + 1);
- }
- else if (mantissa.length - 2 > e) { // Must re-insert decimal
- expanded = expanded.substring(0, e + 1) + '.' + expanded.substring(e + 1);
- }
+ else if (mantissa.length - 2 > e) { // Must re-insert decimal
+ expanded = expanded.substring(0, e + 1) + '.' + expanded.substring(e + 1);
}
}
return expanded;
// Excel converted to scientific notation
try {
const expanded = expandScientificNotation(original, true);
- if (expanded === null) { throw new Error(); }
return BigInt(expanded).toString();
} catch { }
import { suite, test } from 'node:test';
-import { deepStrictEqual } from 'node:assert/strict';
+import { deepStrictEqual, throws } from 'node:assert/strict';
import { csvStreamEqualWritable, createCSVMockStream, assertConsole } from './utils.js';
import { CSVReader } from '../src/core.js';
suite('expandScientificNotation', { concurrency: true }, () => {
const vectors = [
- { name: 'returns null for string integer', input: '123', output: null },
- { name: 'returns null for string BigInt', input: '123n', output: null },
- { name: 'returns null for string number', input: '123.456', output: null },
- { name: 'returns null for string non-numeric', input: 'abc', output: null },
- { name: 'returns null for number', input: 123, output: null },
- { name: 'returns null for BigInt', input: 123n, output: null },
- { name: 'returns null for array', input: [], output: null },
- { name: 'returns null for object', input: {}, output: null },
+ { name: 'throws SyntaxError for string integer', input: '123', error: SyntaxError },
+ { name: 'throws SyntaxError for string BigInt', input: '123n', error: SyntaxError },
+ { name: 'throws SyntaxError for string number', input: '123.456', error: SyntaxError },
+ { name: 'throws SyntaxError for string non-numeric', input: 'abc', error: SyntaxError },
+ { name: 'throws TypeError for number', input: 123, error: TypeError },
+ { name: 'throws TypeError for BigInt', input: 123n, error: TypeError },
+ { name: 'throws TypeError for array', input: [], error: TypeError },
+ { name: 'throws TypeError for object', input: {}, error: TypeError },
{ name: 'returns expanded form for positive exponent', input: '1E18', output: '1000000000000000000' },
{ name: 'returns expanded form for positive exponent (with plus)', input: '1E+18', output: '1000000000000000000' },
{ name: 'returns expanded form for negative mantissa', input: '-1E18', output: '-1000000000000000000' },
{ name: 'truncates when decimals greater than exponent by 1', input: '1.111E2', truncate: true, output: '111' },
{ name: 'truncates when decimals greater than exponent by 2', input: '1.1111E2', truncate: true, output: '111' },
];
- for (const { name, input, truncate = false, output } of vectors) {
+ for (const { name, input, truncate = false, output, error } of vectors) {
test(name, { concurrency: true }, () => {
- deepStrictEqual(expandScientificNotation(input, truncate), output);
+ if (error !== undefined) {
+ // @ts-expect-error
+ throws(() => expandScientificNotation(input, truncate), error);
+ }
+ else {
+ deepStrictEqual(expandScientificNotation(input, truncate), output);
+ }
});
}
});