mirror of
https://github.com/josdejong/mathjs.git
synced 2025-12-08 19:46:04 +00:00
172 lines
4.9 KiB
JavaScript
172 lines
4.9 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Format a number using methods toPrecision, toFixed, toExponential.
|
|
* @param {number | string} value
|
|
* @constructor
|
|
*/
|
|
function NumberFormatter (value) {
|
|
// parse the input value
|
|
var match = String(value).toLowerCase().match(/^0*?(-?)(\d+\.?\d*)(e([+-]?\d+))?$/);
|
|
if (!match) {
|
|
throw new SyntaxError('Invalid number');
|
|
}
|
|
|
|
var sign = match[1];
|
|
var coefficients = match[2];
|
|
var exponent = parseFloat(match[4] || '0');
|
|
|
|
var dot = coefficients.indexOf('.');
|
|
exponent += (dot !== -1) ? (dot - 1) : (coefficients.length - 1);
|
|
|
|
this.sign = sign;
|
|
this.coefficients = coefficients
|
|
.replace('.', '') // remove the dot (must be removed before removing leading zeros)
|
|
.replace(/^0*/, function (zeros) {
|
|
// remove leading zeros, add their count to the exponent
|
|
exponent -= zeros.length;
|
|
return '';
|
|
})
|
|
.replace(/0*$/, '') // remove trailing zeros
|
|
.split('');
|
|
|
|
if (this.coefficients.length === 0) {
|
|
this.coefficients.push(0);
|
|
exponent++;
|
|
}
|
|
|
|
this.exponent = exponent;
|
|
}
|
|
|
|
/**
|
|
* Format a number with fixed notation.
|
|
* @param {Number} [precision=0] Optional number of decimals after the
|
|
* decimal point. Zero by default.
|
|
*/
|
|
NumberFormatter.prototype.toFixed = function (precision) {
|
|
if (precision) {
|
|
var dot = this.exponent > 0 ? this.exponent : 0;
|
|
var all = init(-this.exponent, 0).concat(this.coefficients);
|
|
var coefficients = round(all, dot + precision + 1);
|
|
coefficients.splice(dot + 1, 0, '.');
|
|
|
|
return this.sign + coefficients.join('');
|
|
}
|
|
else {
|
|
// TODO: make the || '0' redundant
|
|
return this.sign + round(this.coefficients, this.exponent + 1).join('') || '0';
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Format a number in exponential notation. Like '1.23e+5', '2.3e+0', '3.500e-3'
|
|
* @param {Number} [precision] Number of digits in formatted output.
|
|
* If not provided, the maximum available digits
|
|
* is used.
|
|
*/
|
|
NumberFormatter.prototype.toExponential = function (precision) {
|
|
var coefficients = precision
|
|
? round(this.coefficients, precision)
|
|
: this.coefficients.slice(0);
|
|
|
|
var first = coefficients.shift();
|
|
return this.sign +
|
|
first +
|
|
(coefficients.length > 0 ? ('.' + coefficients.join('')) : '') +
|
|
'e' + (this.exponent >= 0 ? '+' : '') + this.exponent;
|
|
};
|
|
|
|
/**
|
|
* Format a number with a certain precision
|
|
* @param {Number} [precision=undefined] Optional number of digits.
|
|
* @param {{lower: number | undefined, upper: number | undefined}} [options]
|
|
* By default:
|
|
* lower = 1e-3 (excl)
|
|
* upper = 1e+5 (incl)
|
|
* @return {string}
|
|
*/
|
|
NumberFormatter.prototype.toPrecision = function(precision, options) {
|
|
// determine lower and upper bound for exponential notation.
|
|
var lower = (options && options.lower !== undefined) ? options.lower : 1e-3;
|
|
var upper = (options && options.upper !== undefined) ? options.upper : 1e+5;
|
|
|
|
var abs = Math.abs(Math.pow(10, this.exponent));
|
|
if (abs < lower || abs >= upper) {
|
|
// exponential notation
|
|
return this.toExponential(precision);
|
|
}
|
|
else {
|
|
var all = init(-this.exponent, 0).concat(this.coefficients);
|
|
|
|
var coefficients = precision
|
|
? round(all, precision + (this.exponent < 0 ? -this.exponent : 0))
|
|
: all;
|
|
|
|
// append trailing zeros
|
|
var trailing = init(this.exponent - coefficients.length + 1, 0);
|
|
coefficients = coefficients.concat(trailing);
|
|
|
|
var dot = this.exponent > 0 ? this.exponent : 0;
|
|
if (dot < coefficients.length - 1) {
|
|
coefficients.splice(dot + 1, 0, '.');
|
|
}
|
|
|
|
return this.sign + coefficients.join('');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Round an array with coefficients. For example:
|
|
*
|
|
* round([2,3,4,5,6], 2) returns '23'
|
|
* round([2,3,4,5,6], 4) returns '2346'
|
|
* round([2,3], 4) returns '2300'
|
|
*
|
|
* @param {number[]} coefficients
|
|
* @param {number} count
|
|
* @return {number[]} Returns array with rounded coefficients
|
|
*/
|
|
function round(coefficients, count) {
|
|
// TODO: simplify this method, write in a more compact way
|
|
var rounded = coefficients.slice(0, count);
|
|
if (coefficients[count] >= 5) {
|
|
if (count === 0) {
|
|
rounded.unshift(0);
|
|
count++;
|
|
}
|
|
rounded[count - 1]++;
|
|
|
|
var i = count - 1;
|
|
while (i > 0) {
|
|
if (rounded[i] === 10) {
|
|
rounded[i] = 0;
|
|
|
|
if (i === 0) {
|
|
rounded.unshift(0);
|
|
i++;
|
|
}
|
|
rounded[i - 1]++;
|
|
}
|
|
i--;
|
|
}
|
|
}
|
|
|
|
return rounded.concat(init(count - rounded.length, 0));
|
|
}
|
|
|
|
/**
|
|
* Initialize an array with a certain length and initialize with values.
|
|
* @param {number} length
|
|
* @param {number} value
|
|
* @return {Array}
|
|
*/
|
|
function init(length, value) {
|
|
var arr = [];
|
|
for (var i = 0; i < length; i++) {
|
|
arr.push(value);
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
module.exports = NumberFormatter;
|