Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Tuesday, February 19, 2008

Numbers And Number Format Function

All numbers in JavaScript are 64bit (8 bytes) floating point numbers which yields an effective range of 5e-324 (negative) to 1.7976931348623157e+308 (positive). Because JavaScript is loosely typed and the plus operator also concatenates, you can easily convert JavaScript Numbers to Strings using: 1 + “”. JavaScript also has some built in number logic attached to the Number Object. However, most people never realize this, as they do not understand the difference between a JavaScript Number literal and a Number object.

Example 1: Literal vs. Object

var numericLiteral = 0; var numericObject = new Number(0); if (numericLiteral) { } // false because 0 is a falsy, will not be executed. if (numericObject) { } // true because numericObject exists as an object, will be executed.

The important distinction, as seen in the example above, is that Number objects are not falsy when they are ZERO. Since, they are Objects, they are truthy when they have any value except undefined and null. By default all Number variables (both numbericLiteral and numbericObject) have several functions available on their prototype:

Example 2: Number Functions

method IE FireFox Explaination toExponential 5.5 1.5 Returns the expotential value of the number. toFixed 5.5 1.5 Returns a number with a specified number of decimals. toLocaleString 3 2.0 Displays the number using regional preferences. toPrecision 5.5 1.5 Returns a string with a specified number of digits. toSource — 1.5 Returns the source code used to make the number. toString 3 2.0 Returns the number as a string. ValueOf 3 2.0 See toString

* Table From The Complete JavaScript Number Reference

The ‘toFixed’ and ‘toPrecision’ method come in very handy when converting Numbers to Strings with a given number of decimals or precision. However, they do not help when you want to use commas to represent thousands, millions, etc. For this task we will need to attach another method ‘format’ to Number, which will allow us to add not only comma, but also additional non-number characters (like ‘$’ and ‘%’).

Example 3: Format Function

/** * Formats the number according to the ‘format’ string; adherses to the american number standard where a comma is inserted after every 3 digits. * note: there should be only 1 contiguous number in the format, where a number consists of digits, period, and commas * any other characters can be wrapped around this number, including ‘$’, ‘%’, or text * examples (123456.789): * ‘0′ - (123456) show only digits, no precision * ‘0.00′ - (123456.78) show only digits, 2 precision * ‘0.0000′ - (123456.7890) show only digits, 4 precision * ‘0,000′ - (123,456) show comma and digits, no precision * ‘0,000.00′ - (123,456.78) show comma and digits, 2 precision * ‘0,0.00′ - (123,456.78) shortcut method, show comma and digits, 2 precision * * @method format * @param format {string} the way you would like to format this text * @return {string} the formatted number * @public */ Number.prototype.format = function(format) { if (! isType(format, ’string’)) {return ”;} // sanity check var hasComma = -1 < format.indexOf(’,'), psplit = format.stripNonNumeric().split(’.'), that = this; // compute precision if (1 < psplit.length) { // fix number precision that = that.toFixed(psplit[1].length); } // error: too many periods else if (2 < psplit.length) { throw(’NumberFormatException: invalid format, formats should have no more than 1 period: ‘ + format); } // remove precision else { that = that.toFixed(0); } // get the string now that precision is correct var fnum = that.toString(); // format has comma, then compute commas if (hasComma) { // remove precision for computation psplit = fnum.split(’.'); var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3; // n cannot be ZERO or causes infinite loop // break the number into chunks of 3 digits; first chunk may be less than 3 for (var i = 0; i < j; i += n) { if (i != 0) {n = 3;} parr[parr.length] = cnum.substr(i, n); m -= 1; } // put chunks back together, separated by comma fnum = parr.join(’,'); // add the precision back in if (psplit[1]) {fnum += ‘.’ + psplit[1];} } // replace the number portion of the format with fnum return format.replace(/[\d,?\.?]+/, fnum); };

See the comment block for help on how this function should behave. Basically, you can pass in a String as the format that contain any one number (in the format that you like) and the result will replace the number in the format String with the properly formatted Number object.

****** Update ******

All number variables have the Functions of “Number.prototype” available to them whether instantiated by call “new Number()” or simply setting a primitive to a variable. Only number primitives like “77″ do not have the prototype function.

posted by Matt Snider at 2:59 pm  

5 Comments »

  1. While you are correct on the truthiness aspect, I think it’s misleading to say “The second distinction is that a Number object has several functions available on its prototype.” These functions are also available to the Number literal.

    While the truthiness aspect is true…with numbers I’d argue it’s more correct to code with equality operators, that are explicit in their meaning. “if(numLiteral === 0)” checks specifically whether or not the numLiteral is 0. However, the same statement with numObject, would be false, as the === operator doesn’t do the conversion from a number object to a number literal. The == operator will be true (assuming both numbers are equal to zero).

    I generally avoid using Number Objects, mostly out of personal preference. However, one potential advantage they do have, is you can add attributes to Number Objects, but not Number literals. So numberObject.x = 2 would be okay…numberLiteral.x = 2, probably won’t get a syntax error, but numberLiteral.x will still be undefined. I haven’t ever had to add an attribute to a number object, but that doesn’t mean it couldn’t be useful.

    I’m not a fan of extending native objects, but to each his own. Otherwise, good read.

    Comment by MillsJROSS — February 26, 2008 @ 8:24 am

  2. Mills, You are right about equality being difficult with Number Objects. However, a simple equality function like

    .equals(o) {
    return this.valueOf() === o.valueOf();
    }

    usually does the trick. However, it also turns out that JavaScript does apply all methods attached to the Number.prototype to any Number variable automatically. So you can do:

    var i = 1;
    i.format(’$0,0′);

    but you can’t do

    1.toFixed(’$0,0′);

    As such, there is little reason to force cast to the Number Object.

    Comment by Matt Snider — February 28, 2008 @ 6:29 pm

  3. The final replace regexp is wrong

    return format.replace(/[d,?.?]+/, fnum);

    Comment by Gary — March 4, 2008 @ 5:40 am

  4. Thanks Gary, I forget to escape my backslashes. It is fixed now.

    Comment by Matt Snider — March 4, 2008 @ 9:21 am

  5. Hi Matt. Your number formatting function looks to be just what I need. I am fairly new to Javascript but have written a fairly complex (to me) online calculator and I would like to use your function to properly format the output using the 0,000.00 format. Could you clarify how I pass parameters to the routine and how I define the routine within my HTML page. As I said, I am fairly new to Javascript so apologies for asking a rather basic question. If you want to see the actual calculator, it is “SOA Calc” under the calculator menu from the homepage.
    Thanks
    Clariman

    Comment by Clariman — March 30, 2008 @ 3:10 am

RSS feed for comments on this post. TrackBack URI

Leave a comment

Powered by WordPress