format_double.js (3152B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2022 The Stdlib Authors. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 'use strict'; 20 21 // MODULES // 22 23 var isNumber = require( './is_number.js' ); 24 25 // NOTE: for the following, we explicitly avoid using stdlib packages in this particular package in order to avoid circular dependencies. 26 var abs = Math.abs; // eslint-disable-line stdlib/no-builtin-math 27 var lowercase = String.prototype.toLowerCase; 28 var uppercase = String.prototype.toUpperCase; 29 var replace = String.prototype.replace; 30 31 32 // VARIABLES // 33 34 var RE_EXP_POS_DIGITS = /e\+(\d)$/; 35 var RE_EXP_NEG_DIGITS = /e-(\d)$/; 36 var RE_ONLY_DIGITS = /^(\d+)$/; 37 var RE_DIGITS_BEFORE_EXP = /^(\d+)e/; 38 var RE_TRAILING_PERIOD_ZERO = /\.0$/; 39 var RE_PERIOD_ZERO_EXP = /\.0*e/; 40 var RE_ZERO_BEFORE_EXP = /(\..*[^0])0*e/; 41 42 43 // MAIN // 44 45 /** 46 * Formats a token object argument as a floating-point number. 47 * 48 * @private 49 * @param {Object} token - token object 50 * @throws {Error} must provide a valid floating-point number 51 * @returns {string} formatted token argument 52 */ 53 function formatDouble( token ) { 54 var digits; 55 var out; 56 var f = parseFloat( token.arg ); 57 if ( !isFinite( f ) ) { // NOTE: We use the global `isFinite` function here instead of `@stdlib/math/base/assert/is-finite` in order to avoid circular dependencies. 58 if ( !isNumber( token.arg ) ) { 59 throw new Error( 'invalid floating-point number. Value: ' + out ); 60 } 61 // Case: NaN, Infinity, or -Infinity 62 f = token.arg; 63 } 64 switch ( token.specifier ) { 65 case 'e': 66 case 'E': 67 out = f.toExponential( token.precision ); 68 break; 69 case 'f': 70 case 'F': 71 out = f.toFixed( token.precision ); 72 break; 73 case 'g': 74 case 'G': 75 if ( abs( f ) < 0.0001 ) { 76 digits = token.precision; 77 if ( digits > 0 ) { 78 digits -= 1; 79 } 80 out = f.toExponential( digits ); 81 } else { 82 out = f.toPrecision( token.precision ); 83 } 84 if ( !token.alternate ) { 85 out = replace.call( out, RE_ZERO_BEFORE_EXP, '$1e' ); 86 out = replace.call( out, RE_PERIOD_ZERO_EXP, 'e'); 87 out = replace.call( out, RE_TRAILING_PERIOD_ZERO, '' ); 88 } 89 break; 90 default: 91 throw new Error( 'invalid double notation. Value: ' + token.specifier ); 92 } 93 out = replace.call( out, RE_EXP_POS_DIGITS, 'e+0$1' ); 94 out = replace.call( out, RE_EXP_NEG_DIGITS, 'e-0$1' ); 95 if ( token.alternate ) { 96 out = replace.call( out, RE_ONLY_DIGITS, '$1.' ); 97 out = replace.call( out, RE_DIGITS_BEFORE_EXP, '$1.e' ); 98 } 99 if ( f >= 0 && token.sign ) { 100 out = token.sign + out; 101 } 102 out = ( token.specifier === uppercase.call( token.specifier ) ) ? 103 uppercase.call( out ) : 104 lowercase.call( out ); 105 return out; 106 } 107 108 109 // EXPORTS // 110 111 module.exports = formatDouble;