diff --git a/lib/node_modules/@stdlib/string/base/format-interpolate/lib/format_double.js b/lib/node_modules/@stdlib/string/base/format-interpolate/lib/format_double.js index e6d3d9ebc261..64fb5db60b33 100644 --- a/lib/node_modules/@stdlib/string/base/format-interpolate/lib/format_double.js +++ b/lib/node_modules/@stdlib/string/base/format-interpolate/lib/format_double.js @@ -21,7 +21,6 @@ // MODULES // // NOTE: for the following, we explicitly avoid using stdlib packages in this particular package in order to avoid circular dependencies. -var abs = Math.abs; // eslint-disable-line stdlib/no-builtin-math var lowercase = String.prototype.toLowerCase; var uppercase = String.prototype.toUpperCase; var replace = String.prototype.replace; @@ -31,9 +30,11 @@ var replace = String.prototype.replace; var RE_EXP_POS_DIGITS = /e\+(\d)$/; var RE_EXP_NEG_DIGITS = /e-(\d)$/; -var RE_ONLY_DIGITS = /^(\d+)$/; -var RE_DIGITS_BEFORE_EXP = /^(\d+)e/; -var RE_TRAILING_PERIOD_ZERO = /\.0$/; +var RE_ONLY_DIGITS = /^(-?\d+)$/; +var RE_DIGITS_BEFORE_EXP = /^(-?\d+)e/; +var RE_EXP = /e([+-]\d+)$/; +var RE_TRAILING_ZEROS = /(\.\d*?)0+$/; +var RE_TRAILING_PERIOD = /\.$/; var RE_PERIOD_ZERO_EXP = /\.0*e/; var RE_ZERO_BEFORE_EXP = /(\..*[^0])0*e/; @@ -51,6 +52,7 @@ var RE_ZERO_BEFORE_EXP = /(\..*[^0])0*e/; */ function formatDouble( f, token ) { var digits; + var exp; var out; switch ( token.specifier ) { @@ -64,19 +66,23 @@ function formatDouble( f, token ) { break; case 'g': case 'G': - if ( abs( f ) < 0.0001 ) { - digits = token.precision; - if ( digits > 0 ) { - digits -= 1; + // A precision of zero is treated as one significant digit: + digits = ( token.precision > 0 ) ? token.precision : 1; + + // Use the exponent of the value rounded to `digits` significant digits to choose between exponential and fixed-point notation, as in C's `printf`: + out = f.toExponential( digits - 1 ); + exp = RE_EXP.exec( out ); + if ( exp ) { + exp = parseInt( exp[ 1 ], 10 ); + if ( exp >= -4 && exp < digits ) { + out = f.toFixed( digits - 1 - exp ); } - out = f.toExponential( digits ); - } else { - out = f.toPrecision( token.precision ); } if ( !token.alternate ) { out = replace.call( out, RE_ZERO_BEFORE_EXP, '$1e' ); out = replace.call( out, RE_PERIOD_ZERO_EXP, 'e' ); - out = replace.call( out, RE_TRAILING_PERIOD_ZERO, '' ); + out = replace.call( out, RE_TRAILING_ZEROS, '$1' ); + out = replace.call( out, RE_TRAILING_PERIOD, '' ); } break; default: diff --git a/lib/node_modules/@stdlib/string/base/format-interpolate/test/test.js b/lib/node_modules/@stdlib/string/base/format-interpolate/test/test.js index 9123cb023180..e05b17e5fdb0 100644 --- a/lib/node_modules/@stdlib/string/base/format-interpolate/test/test.js +++ b/lib/node_modules/@stdlib/string/base/format-interpolate/test/test.js @@ -796,6 +796,123 @@ tape( 'the function returns a formatted string (`G` specifier, alternate form)', t.end(); }); +tape( 'the function returns a formatted string (`g` specifier, trailing zeros)', function test( t ) { + var expected; + var values; + var actual; + var tokens; + var i; + + tokens = formatTokenize( '%g' ); + values = [ 1.5, -2.5, 100, 0.5, 0.0001, 123.0, 1.0e-5 ]; + expected = [ '1.5', '-2.5', '100', '0.5', '0.0001', '123', '1e-05' ]; + for ( i = 0; i < values.length; i++ ) { + actual = formatInterpolate( tokens, values[ i ] ); + t.strictEqual( actual, expected[ i ], 'returns expected output' ); + } + + tokens = formatTokenize( '%.3g' ); + actual = formatInterpolate( tokens, 0.1 ); + expected = '0.1'; + t.strictEqual( actual, expected, 'returns expected output' ); + + tokens = formatTokenize( '%10g|' ); + actual = formatInterpolate( tokens, 1.5 ); + expected = ' 1.5|'; + t.strictEqual( actual, expected, 'returns expected output' ); + + tokens = formatTokenize( '%G' ); + actual = formatInterpolate( tokens, 0.5 ); + expected = '0.5'; + t.strictEqual( actual, expected, 'returns expected output' ); + + t.end(); +}); + +tape( 'the function returns a formatted string (`g` specifier, zero)', function test( t ) { + var expected; + var tokens; + var actual; + + tokens = formatTokenize( '%g' ); + actual = formatInterpolate( tokens, 0 ); + expected = '0'; + t.strictEqual( actual, expected, 'returns expected output' ); + + tokens = formatTokenize( '%#g' ); + actual = formatInterpolate( tokens, 0 ); + expected = '0.00000'; + t.strictEqual( actual, expected, 'returns expected output' ); + + t.end(); +}); + +tape( 'the function returns a formatted string (`g` specifier, zero precision)', function test( t ) { + var expected; + var tokens; + var actual; + + tokens = formatTokenize( '%.0g' ); + actual = formatInterpolate( tokens, 1.5 ); + expected = '2'; + t.strictEqual( actual, expected, 'returns expected output' ); + + tokens = formatTokenize( '%.0g' ); + actual = formatInterpolate( tokens, 15 ); + expected = '2e+01'; + t.strictEqual( actual, expected, 'returns expected output' ); + + t.end(); +}); + +tape( 'the function returns a formatted string (`g` specifier, switching between fixed-point and exponential notation)', function test( t ) { + var expected; + var tokens; + var actual; + + tokens = formatTokenize( '%g' ); + actual = formatInterpolate( tokens, 123456 ); + expected = '123456'; + t.strictEqual( actual, expected, 'returns expected output' ); + + actual = formatInterpolate( tokens, 1234567 ); + expected = '1.23457e+06'; + t.strictEqual( actual, expected, 'returns expected output' ); + + actual = formatInterpolate( tokens, 999999.5 ); + expected = '1e+06'; + t.strictEqual( actual, expected, 'returns expected output' ); + + actual = formatInterpolate( tokens, 0.00009999995 ); + expected = '0.0001'; + t.strictEqual( actual, expected, 'returns expected output' ); + + t.end(); +}); + +tape( 'the function returns a formatted string (`g` specifier, alternate form, negative values)', function test( t ) { + var expected; + var tokens; + var actual; + + tokens = formatTokenize( '%#.3g' ); + actual = formatInterpolate( tokens, -340.12 ); + expected = '-340.'; + t.strictEqual( actual, expected, 'returns expected output' ); + + tokens = formatTokenize( '%#g' ); + actual = formatInterpolate( tokens, -1.5 ); + expected = '-1.50000'; + t.strictEqual( actual, expected, 'returns expected output' ); + + tokens = formatTokenize( '%#.0f' ); + actual = formatInterpolate( tokens, -3 ); + expected = '-3.'; + t.strictEqual( actual, expected, 'returns expected output' ); + + t.end(); +}); + tape( 'the function returns a formatted string (`f` specifier, minimum width)', function test( t ) { var expected; var tokens;