The other day, someone at the office asked me about adding parenthesis to his ColdFusion NumberFormat() function mask. I couldn't help him. I pretty much only ever use NumberFormat() to add mandatory decimal points and decimal places; in fact, I really use NumberFormat() to do what ColdFusion's DecimalFormat() function already does - I should really revaluate what I am doing in that case. Anyway, it occurred to me that most mask constructs of the NumberFormat() function are a total mystery to me. Time to do a little exploration.
One of the first things that I noticed in the ColdFusion NumberFormat() documentation is that if the value passed to NumberFormat() is a empty string, ColdFusion will convert it to 0. Therefore, if running this code:
Launch code in new window » Download code as text file »
... we get the following output:
0.00
I am not really sure what the function of this feature is for? I could understand if NumberFormat() would convert any non-numeric string to zero, but it doesn't. If I tried to run this:
Launch code in new window » Download code as text file »
... I would get the following ColdFusion error:
The value "ben" cannot be converted to a number.
So, it converts empty string, but I am probably never going to use that feature. That's probably more of a side effect of some internal process or something? I can't imagine they would have decided that empty string was OK as zero, but other strings were not.
That aside, what we really want to concentrate on are the available number masks. ColdFusion provides several special mask characters:
_ OR 9 - Digit placeholder - if there is a matching digit in the passed in number, it is displayed. Both _ and 9 do the same thing, only 9 shows decimal places a bit more clearly seen than the _ character. This mask item will not add any leading digits that are not in the given number - it will only display the leading digits that are already present. It will, however add trailing digits. Of course, if a digit is not present in the original value, it can only append zeros.
Running this code:
Launch code in new window » Download code as text file »
... gives us this output:
4.00
Notice that the 9 in the tens place did not add any leading zeros, but the 99 after the decimal place did add trailing zeros.
. - The period is the existing of or the location of a mandatory decimal point. This construct is used primarily in conjunction with other number masks. Used alone, it doesn't make a lot of sense. Running this code:
Launch code in new window » Download code as text file »
... gives us this output:
4.
Notice that the "." is now mandatory, but at the same time it doesn't make sense. In fact, I am not even sure if this is considered a valid number??? Really, the decimal place is meant to be used with something like the 0 mask of or the 9 mask. So, for instance, if we wanted to always show a number that had one decimal place after the zero, we could run this:
Launch code in new window » Download code as text file »
This could give us the following output:
3.2
There's a few things to notice here. For starters, the 9 mask, when right of the decimal place will only show digits that match the mask. Any digits that appear right of the 9 number mask gets truncated. Also notice that when this truncation occurs, the number is rounded: .16 rounds up to be .2 in NumberFormat(). More importantly, realize that the 9 behaves in this way because of its placement in relation to the mandatory decimal place.
0 - The zero is a digit placeholder that does the same thing that the 9 mask does, only the zero mask will add leading zeros (unlike the 9 mask which will only add trailing zeros). Therefore, running this code:
Launch code in new window » Download code as text file »
... we get the following output:
007.400
Notice that not only where the 7 and 4 digits show, the 0 mask added two leading zero and two trailing zeros. This is the mask construct that I use most often to do exactly what decimal format does:
Launch code in new window » Download code as text file »
This forces the thousands separator, the mandatory decimal point, and two decimal places at all times. Like I said, I should really just be using ColdFusion's DecimalFormat() to do this.
( ) - This places parentheses around a number if that number is less than zero. Therefore, running this code:
Launch code in new window » Download code as text file »
... gives us this output:
(6.30)
Notice that instead of using the minus sign, the 6.30 is placed in the parentheses. Also notice that our () mask does not surround the zeros in the number mask. This is not a requirement - NumberFormat() is just looking for mask "queues" and this one does NOT depend on placement (meaning, the placement of the () in the NumberFormat() mask should not ever change the output of the function). You can of course, use the parenthesis in a more readable format:
Launch code in new window » Download code as text file »
... but this is just a matter of personal preference; they both produce the same output.
If the number is a positive number then the function simply ignores the parentheses in the resultant output.
+ - The plus sign puts a plus sign before positive numbers and a minus sign before negative numbers. On it's own this is not so powerful, but when used in conjunction with other number masks, this will help to make sure that every value returned from ColdFusion's NumberFormat() is always the same length when either positive or negative in value. Running this code:
Launch code in new window » Download code as text file »
... we get the following output:
+5
And, running this code:
Launch code in new window » Download code as text file »
... we get the following output:
-5
Again, I think the real benefit of this is that we always get the same length value. Of course, if we had some sort of ledger system, the +/- would be a nice denoting of credit and debit.
- - Puts a space before positive numbers and a minus sign before negative numbers. Like the "+" number mask, this sort of mask allows us to produce values that are always the same length, whether positive or negative. Running this code:
Launch code in new window » Download code as text file »
... we get the following output:
[ 8]
Notice the leading space. And, just to drive home the point that NumberFormat() is looking for mask "queues", the minus sign or plus sign do NOT have to do at the beginning of your mask. For instance, running this code:
Launch code in new window » Download code as text file »
... we get the following output:
[ 8.00]
Notice that after the 0.00 mask was applied, the "-" mask was properly applied even though it came after the mandatory decimal places. The position of the +/-, like the "()" mask construct, is not impacted by its placement and should be done so according to personal preference.
, - Separates every third decimal place with a comma (sometimes referred to as the thousands separator). Therefore, running the following code:
Launch code in new window » Download code as text file »
... gives us the following output:
123,456,789
L,C,R - These position the value within in the number mask. L left justifies the value. C center justifies the value. R right justifies the value. These masks only have an affect when the digit place holder 9 (or .) is being used. If you use the 0 (zero) mask, these queues get ignored. In the following code, I am using ColdFusion's Replace() method to replace out spaces with * so that we can actually see how the replace works. Running this code:
Launch code in new window » Download code as text file »
... we get the following output:
9****
**9**
****9
You can use this mask in conjunction with other masks, but things are a bit unpredictable.
$ - Puts a dollar sign before formatted number. The document states that the first character of mask must be the dollar sign, bit in my experience, this is not true. Like many of the other masks used in ColdFusion's NumberFormat(), it is the existence of the $ that is important, not its placement. Running this code:
Launch code in new window » Download code as text file »
... gives us the following output:
$23
Now, when using this in conjunction, things are little hairy at times. For instance, if we used this with a negative number:
Launch code in new window » Download code as text file »
... we get:
-$23
Notice that the minus sign comes before the $ sign (not exactly what the documentation says it will do). However, if we add the parentheses mask:
Launch code in new window » Download code as text file »
... we get the following output:
$(23)
In this scenario, the dollar sign is outside of the parentheses even though in the previous example, the $ sign was in between the "negative number notation" and the value. Not sure if that seems inconsistent or not, just take care when combining masks. Notice also that the placement of the $, contrary to the documentation, is not important.
^ - This allows you to create different formatting for the left and right of the decimal place. To be honest, I could not figure out how this one works :)
Ok, so that gives me some more information about ColdFusion's NumberFormat(). Not a lot of surprises here, but I did learn some neat stuff that I might be able to apply at some point.
Download Code Snippet ZIP File
Comments (8) | Post Comment | Ask Ben | Permalink | Print Page
Ask Ben: Selecting All Text Nodes Of An XML Document
Foundeo's CFImageEffects Component For ColdFusion 8
One "feature" of NumberFormat that I have used is to convert ColdFusion boolean values to 1 or 0. This comes in handy when passing these values to SQL/JS/xml when you want a consistent format.
NumberFormat( "true" ) = 1
NumberFormat( "false" ) = 0
I have secretly fantasized that Val("true") would return 1... but alas it always returns 0 for any string.
Great summary.
Posted by Brett on Aug 13, 2007 at 10:00 AM
@Brett,
Val("true") might return 0, but (true * 1) will return 1.
Posted by Ben Nadel on Aug 13, 2007 at 10:05 AM
I'd imagine the #NumberFormat( "", "0.00" )# returning 0 is for when you are pulling data out of a query that has nulls. If it didn't you'd have to always test for "" since CF converts null values to empty strings.
Just my $.02
Posted by Dustin on Aug 13, 2007 at 10:18 AM
Nice... But... I'm still one step away though. What I am looking for is a single function call that will evaluate a loosely typed variable as a bit, while handling CFs three "yes,true,1" in the same way.
Val ( "true" * 1 ) works nicely, but Val ( "string" * 1 ) throws an error.
Posted by Brett on Aug 13, 2007 at 10:20 AM
Brett - I believe if you add YesNoFormat(), it should handle all the boolean options:
#NumberFormat(YesNoFormat("1"))# = 1
#NumberFormat(YesNoFormat("true"))# = 1
#NumberFormat(YesNoFormat("yes"))# = 1
#NumberFormat(YesNoFormat("0"))# = 0
#NumberFormat(YesNoFormat("false"))# = 0
#NumberFormat(YesNoFormat("no"))# = 0
#NumberFormat(YesNoFormat(""))# = 0 (it even works for blank)
Posted by Adam Fairbanks (Tidy Internet) on Aug 14, 2007 at 1:48 PM
Adam - I hope you are subscribed to this thread!
I've been using your method for a bit, thanks btw! But alas, I found the next problem in it.... (I have a huge rant about cf functions brewing...)
This throws an error (cf8 j2ee), so close!
<cfset myVal = "brett">
<cfif NumberFormat(YesNoFormat( myVal ) ) >
<cfdump var="yep">
<cfelse>
<cfdump var="nope">
</cfif>
Posted by Brett S. on Jun 11, 2008 at 11:27 PM
I guess this is the final statement - boy aint CF kinky!
NumberFormat( YesNoFormat( val ( myVar ) ) )
AND
NumberFormat( YesNoFormat( val ( "my valuel" ) ) )
Posted by Brett S. on Jun 11, 2008 at 11:31 PM
@Brett,
ColdFusion is usually pretty good about converting back and fourth between strings and numbers. For the YesNoFormat() and NumberFormat(), I would assume that it would auto convert "Yes" to "1". Is that where the error is coming in? Or is the error happening at YesNoFormat() itself?
Posted by Ben Nadel on Jun 12, 2008 at 7:33 AM