The problems with toFixed() in JavaScript


This tutorial requires an intermediate understanding of JavaScript, HTML, and CSS.

So, what’s wrong with toFixed()?
I do e-commerce websites and often I need to calculate cost amounts like product pricing, shipping, and other things. I usually use the toFixed() method to make sure that any numbers I calculate have no more than 2 decimal places. So, a price like 2.033 does not help me. It looks like an error to the user, so I need to round the digits to 2.03 so it will display properly.

The problem with using the toFixed() method is that it tends to convert the number to a string after it concatenates the digits after the decimal place. Then, when I try to do another calculation with the result of the toFixed() method, it generates a NaN error (not a number). I have lost my result and any future calculations fall back to this value of NaN. And not only does it disrupt the number I needed, it also tends to stop your function from completing its task, so the website ends up sitting there doing nothing. Ugh!

So, what to do?
Well, there are certainly times when you want the result to be a string. Maybe you are doing a calculation only once and want to add a prefix string like “$” to it to show “$2.03″, or something else. Great. toFixed() is certainly your guy for that job.

However, I have found that for my needs I really need something that will consistently return a number that I can continue to manipulate as a number datatype and not a string datatype.

My solution was simple: create my own decimal-trimming function. I call it trimNum(). The code below will add it to the prototype object in JavaScript so it will be accessible to all Number objects in your Javascript code. All you need to do to to use it is the following:

yournumnbervariable.trimNum(2);

The result will ALWAYS be a number with no more than the requested decimal places (in this case we wanted 2, but you can specify any number). You can also pass it a second parameter like “ceil” and “floor” so it will use the Math.floor() or Math.ceil() method to round the number. If no second parameter is passed, the method simply falls back to using the standard Math.round() method.

If you want to try it, you can go to this page.

Or you can download the source code here.

The solution…
So, here is the basic code that adds the method to all Number objects:

// a replacement for the toFixed() function in javascript (which seems to have problems)

Number.prototype.trimNum = function(places,rounding){

(rounding != 'floor' && rounding != 'ceil') ? rounding = 'round' : rounding = rounding;

var result, num = this, multiplier = Math.pow( 10,places );

result = Math[rounding](num * multiplier) / multiplier;

return Number( result );

}

You call it simply by doing the following, where num is your number you want trimmed, and places is the number decimal places to trim the number down to, rounding is simply the type of rounding you want. Use ‘floor’ to round number DOWN, ‘ceil’ to round them UP, and ’round’ to use standard rounding where if the number is 5 or more it gets rounded up, and if it is below 5 it gets rounded down. Pretty simple.

usage: num.trimNum( places, rounding );

The variables “places” and “rounding” can be either numbers, or variables. The variable “num” needs to be number to work properly. If you are not sure you can use the parFloat() or parseInt() methods to coerce the number before you pass it to the trimNum() method.

A line-by-line explanation of the code
Here is what the code does line-by-line. After the comment we have the “Number.prototype…” line. This adds the method called “trimNum” to the Javascript prototype object. This will make it available to all current and future Number object in your code.

The next line “(rounding != ‘floor’ && rounding != ‘ceil’)…” is a Ternary way of basically asking if the parameter “rounding” is not equal to “floor” or “ceiling” then it needs to be “round”. This is helpful to make sure the method does not stop working if you misspell the second parameter. Also, it allows us to call the trimNum method using only a single parameter (the number of decimal places we want to trim).

The next line “var result, num = this, multiplier = Math.pow( 10,places )” sets up our initial variables and also makes sure that our multiplier is set to 10 to the power of 10(x) times. So, if you pass a 2 to the method it will use 100 as the basis for extracting the last two decimal places. If you send it a 3 it will use 1000, and so on.

The next line “result = Math[rounding](num * multiplier) / multiplier” simply take the number and multiplies it by the multiplier. It then rounds the result using the appropriate Math.round method and finally, it divides it by the multiplier to put the decimal place back where it belongs.

Good luck!
If you have any improvements on this code, I would love to hear about them. But for now, I hope this helps others avoid the pitfalls of using toFixed() when you need a number instead of a string result. Sure, you could simply take the result of the toFixed() method and convert it to a number, but after the tenth time you need to do that, it gets a little tiresome. Also, the more lines of code you have to generate, the more chances there are for making mistakes in your code. I love the jQuery slogan of “do more, code less”.

About these ads

2 Responses to The problems with toFixed() in JavaScript

  1. How many calories blog says:

    Nice work again…

    I am normally just an observer when it comes to blogs, but this actually made me need to leave a comment. Fantastic work!…

  2. exoboy says:

    Exactly, which is one of the major problems with toFixed(). It needs to be more robust. So, this blog entry is not about replacing the method as much as it is making it more robust. To be a good method it should take into account all possible types of input and ways of handling each. Thanks for the comment, though!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: