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

iPhone Fix-It List

General Observation, Hardware, Software
My handy device needs more.

My handy iPhone needs more handy features

I like my iPhone and my wife likes her iPhone and we both have the 1st generation iPhone and don’t really miss the 3G features like the GPS much since we both have Navigation systems in our vehicles.  However, there are plenty of troubles that I’ve come across that just seem to be lost on the developers there in Apple Town.  Also, as a developer, I don’t want to blame everything on them as some lack of features could be a hardware architecture issue, manager’s choice, and/or “let’s hold onto that feature until version X” decisions.

1. Having to reboot

Why am I having to reboot my PHONE!? And every other day no less.  Was this built on the Windows 95 Operating System?  Are we working with Microsoft now, not Apple?  How many fan boys have I read that loved to point out that my Windows 95 machine had to be rebooted so often and here I am now having to reboot my phone.  Yes, I have a bunch of email accounts.  Yes, I have several applications that I’ve downloaded that are free.  But, this rebooting thing has been there since the beginning.

Signs you need to reboot your iPhone

  • Your Blue-tooth starts sounding like your battery is dying (static, etc.) but you just unhooked it fully charged this morning.
  • Your email isn’t checking at the intervals you’ve specified.
  • Your applications fail to load or load and crash even though they worked an hour ago.  (bless you Cuberunner for destroying my waiting room boredom!)
  • Your multi-touch actions start to delay beyond a few milliseconds.  Ugh, trying to turn that thing off when you’ve “swiped” to turn off and have to actually wait up to 10 seconds before the screen reacts is atrocious.

2. Playing songs ‘silently’!

If I can’t play the song and H-E-A-R it, then why play it?  Why give me three minutes of dead silence?  Why s it sync’d to my iPhone in the first place?  Yes, I know I can just skip to the next song when it happens, but good grief, I only have 8GB of space and 800MB of “silent” songs eats into that space pretty hard.  Also, having to fish out my iPhone just to skip past a “silent” song doesn’t seem like the next generation of computing to me (Yes Tech 5 influenced that line).  Here’s a few choices I could come up with for your next update:

  • Tell me the song can’t be played because of that dastardly DRM.
  • Skip the song completely and let me figure out that the song isn’t ever being played because of that dastardly DRM.
  • Don’t let me put DRM songs onto my iPhone, or any i<whatever> through iTunes, that won’t play on that device.  Now there’s an idea.

I’m sure there are a few other options.  That’s why I have a comments section below.

3.  Not checking email!

There are configuration settings for checking email for every account.  My primary email is supposed to be on “push” and I’ve set my check mail settings to “every 15 minutes”.  Too often I pick up my iPhone where the ‘last time email was checked’ value is over 5 hours ago!  I have a lot of email accounts and support a lot of customers through this thing as my primary source of emails to at least know that a problem has occured and I shouldn’t have to handle my iPhone and click on the swirl icon to force the iPhone to catch up on my emails.

4.  Not using Blue-tooth!

I’ve had too many phone calls where I hung up one call and then tapped my next incoming call button with my thumb only to not hear them.  You know why? Because I used my thumb to answer the phone call instead of having to drop my phone so I could tap the answer button on my Blue-tooth.  So my iPhone decided I didn’t want to use my Blue-tooth on my ear.  Why is that?  Why can’t, by default, the phone use the Blue-tooth and let me choose to disable it.  My wife’s van tries to take the phone call over no matter what, so why can’t any of the three Blue-tooth devices I’ve used on it seem to do the same for my iPhone?

5.  Blue-tooth button does what exactly?

I’ve tried to understand the single button on my phone when I’m not dropping my iPhone to answer using that thing.  It’s obvious when the phone rings that it answers the phone.  But what exactly is it supposed to do when I hold it down?

  • Does it call back the person I just talked with?
  • How about the last person I called?
  • Maybe the last person I called while connected to my Blue-tooth?
  • Could it be the last person that called me?
  • Perhaps it calls the next person I should be talking to because it integrated the Genius Feature and it has gone a little overboard with itself?

I certainly don’t know even after reading the directions because I’ve clicked that button and it started calling someone I hadn’t talked to in 3 days on that phone.  I personally liked it with my last phone where it would simply call back the last person I talked with on that phone indiscriminate of whether I called them or they called me first.  That way, if the call dropped I didn’t have to dig up my phone and find their number, just as I don’t have to dig up my phone to answer it.

Let me know what you think.  Comments are welcome.

2 Comments