Browsing the archives for the javascript tag.

Percentage Math in Javascript

Software, Web Development
Uncle Sam

Uncle Sam

We’ve all worked with the Math Objects while doing some kind of addition, subtraction, multiplication, and division to make our websites seem more alive.  Probably not everyone has done Tax math for purchases and refunds.  I’ve recently come across a problem that exposes a little hole in the Javascript world when dealing with percentage math against negative numbers. 

As most of you should be aware, dealing with percentages requires you take the percentage value and then divide that value by one-hundred (100) in order to perform currency math properly.  You must also try to round to two(2) decimal places as well because we don’t charge portions of a cent, only full cents of value.

The problem I’ve come across are the rare (update: not so rare afterall) instances when the negative rounded value for the value of tax is actually a different number than the same math performed on the positive value.  This can cause some pretty irate customers even though we’re talking about a penny’s worth of difference.  I know I would be upset if I saw they were doing bad math because I would be asking myself, “What other areas are they doing this bad math in?  My cellular minutes, my data usage by the megabyte, my water usage, etc.”  That could lead to bad publicity, like being published on the consumerist, and loss of customer confidence in your ability to provide quality service. 

When making a purchase at the local store, or even an online store, if you take a retail item and multiply it’s cost times the total percentage of tax to be included in the transaction you would expect it’s going to be same value if you do the same math with the negative value.  Such as when you needed to return that item to the store for whatever reason.  However, I’ll show you how to find where that may not be the case.  Noteworthy here is to realize we’re talking about a single tax value multiplied against a single currency value.  This may not come up if you were to do multiple tax calculations that should equal up to the same percentage you’re doing here for testing.  For Example: one calculation could be $10.50 against 5% gives you trouble but combining the math of $10.50 against a 3% and 2% calculations do not give you troublesome values. 

Seeing the problem in action

The script below is an example of what a developer might use for calculating taxes which would be a simple rounding after doing a percentage math problem.  However, if you use the value $10.50 and a percentage of 5%.  You’ll get $0.53 as a result, but for the -$10.50 at the same 5% you’ll get -$0.52. 

function testRounding1(RoundingAmount, PercentageAmount )
{
   //calculate and return tax value
   return Math.round(parseFloat(RoundingAmount*(PercentageAmount/100))*100)/100 ;
}

Using this script, you can try several values, both positive and negative to see how many combinations you can come up with that are part of the rounding problem.  I created a script you can use to help yourself find currency and percentage combinations, but beware, this script can crash your browser because of it taking so long.  I try to limit it to just about $10.00 limits at a time because of the amount of time it takes to show the user each “hit”. 

function findTroubleValues()
{
var NextElement = “”;
var intCount = 0;
//start at one cent and increment by one cent until $100.00
for (var dollarValue = 0.01; dollarValue < 100.01;)
{
//start at 0.1% and increment by 0.1% until 50.0%
for (var taxValue = 0.01; taxValue < 50.01;)
{
if ((testRounding1(dollarValue,taxValue)) != (testRounding1(dollarValue * -1,taxValue) * -1))
{
NextElement = “Purchase Amount: $” + dollarValue + ” has: $” + testRounding1(dollarValue,taxValue) + ” vs. $ ” + testRounding1(dollarValue * -1,taxValue) + ” @ ” + taxValue + “%
“;
//output to your test space the value of NextElement
intCount ++;
}
taxValue = Math.round((taxValue*100) + 1)/100;
//used instead of for loop’s taxValue+=0.01
}
dollarValue = Math.round((dollarValue*100) + 1)/100; //same as taxValue
}
alert( “Total HIT Count:” + intCount);
}

Wow! Now that’s a lot of “hits” here to show just how often this problem can rear it’s ugly head.  How can we deal with this problem?  Simple, do positive math all the time and remember to turn it back into negative before returning the value to the calling function.

Using the following script you’ll find that this handles that environment and you don’t have to worry about having to remember “oh yeah, if it’s negative use different test, etc.” in your day to day developing.¦lt;br />  

function computeTaxes(RoundingAmount, PercentageAmount)
{
   var blnNegCheck = new Boolean(false);
   //assume positive value
   var returnValue = 0.00;
   if(parseFloat(RoundingAmount) > 0 )
   {
      returnValue = RoundingAmount;
      blnNegCheck = false;
   }else{
      returnValue = RoundingAmount * -1; //initial value changed to positive
      blnNegCheck = true;
   }
   //initialize the percentage to the math equivelant
   var localPerc = parseFloat(PercentageAmount/100);
   //do the math
   returnValue = Math.round(parseFloat(RoundingAmount*localPerc)*100)/100;
   //return negative values back to negative
   if(blnNegCheck)
   {
      returnValue = returnValue * -1;
   }
return returnValue;
}

As you can see the first thing the code does is set a flag if the value passed is a negative value.  We then convert that negative value to positive, do the math, then return that value to negative before returning it to the calling function.  Performing the normal math without the conversions is done for positive values.  That way no matter what the value the same basic math and rounding is performed in a matching manner.

I have also generated a simple HTML file if you would like to see these items in action.  Email me if you can’t get the file to download.

I hope you’ve enjoyed this lesson and please feel free to give some constructive feedback as I enjoy providing quality information.

3 Comments

Web Server’s Variables

Diagnostics, Software Development, Web Development

Here’s another diagnostic tool for web developers: The Web Server’s Variables.  Constantly there are problems with communications or wondering what has been sent by the user’s website to the server and you sometimes wonder what variables might be useful for our programs.  However, I’ve never been too impressed with Google’s responses when looking for matches between Javascript and VBScript comparison between the two, or useful posts for that fact.  Anyway, before I start rambling about the lack of these kinds of posts, let’s get to the post.

As you may already know, I’m a big XML fan for building information blocks that can be used by my applications or communicating between applications.  So, I’ll be building XML blocks based on the variable name as the tags and the value inside.

VBScript – Server Variables

dim ResponseString
For Each ThisServerVar In Request.ServerVariables
       ResponseString = ResponseString + "<" + ThisServerVar + ">" + _
                                         Request.ServerVariables(ThisServerVar) + _
                                         "</" + ThisServerVar + ">"
Next

Javascript – Server Variables

var responseString = "";
for(var i=1; i<=Request.ServerVariables.Count(); i++)
{
   strKeyName = Request.ServerVariables.Key(i);
   strKeyValue = Request.ServerVariables.Item(strKeyName);
   responseString += "<" + strKeyName + ">" + strKeyValue + "</" + strKeyName + ">";
}

Now I can’t tell you where you will need to use this functionality.  But, it is a good diagnostic tool that can be very helpful from time to time when you’re just poking around trying to determine your communications.  Most of the time, you’ll know the exact server variable you’re looking to use.  But sometimes, when you’re programming in 6+ languages in a single week, you’re likely to forget the exact syntax and it’s nice to have something like this to reference.

Enjoy!

Please feel free to post any comments that others may find useful or even something I could use to update this entry.

2 Comments