Rounding 5 cents DOWN (C# and VFP examples)

In New Zealand small change is changing … we’re getting new 50c, 20c and 10c coins, and the 5c coin is being dropped. To match this the guidelines for rounding cash values are also changing.

Previously, the general guidelines were that values were rounded to the nearest 5c - values ending in 1,2,6,7 rounded down, and those ending in 3,4,8,9 were rounding up.

Now we need to round to 10c. However, the New Zealand Retailers Association guidelines state that values ending in 5c (ie exactly in the middle) are to be rounded DOWN (towards zero). This is not conventional mathematical rounding, that you’d find in a function called round() in most programming languages’ standard libraries. So I had to do some work.

Note there is a difference between rounding down and rounding towards zero. Rounding towards zero is more correct, this way −round(x) is equal to round(−x).

If you’re in the .NET world, and have moved to .NET 2.0, then a quick search of the help might lead you to think this is pretty simple - there’s a function for it, Decimal.Round (Decimal, Int32, MidpointRounding).

Unfortunately there isn’t a rounding option for towards zero, only away from zero. Also, many people (myself included) have applications that are still using .NET framework 1.1, aren’t going to move them 2.0 any time soon. This is what I eventually came up with (in C#):

public static decimal CashRound( decimal exactValue )
{
   if( exactValue >= 0m )
   {
      if( exactValue%0.1m < = 0.05m ) return Decimal.Truncate( exactValue*10 ) /10;
         else return Decimal.Truncate( exactValue*10+1) /10;
   }
   else
   {
      if( exactValue%0.1m >= -0.05m ) return Decimal.Truncate( exactValue*10 ) /10;
         else return Decimal.Truncate( exactValue*10-1) /10;
   }
}

Originally I had to retrofit this rounding rule to a couple of Visual Foxpro programs. It proved a little trickier than I expected, however the following expression does it - and works for currency as well as numeric data type:

cashprice = floor(ceiling( abs(m.exactprice * 10) -.5 )+.5) / 10 * sign(exactprice)

We already had, in our standard library of VFP functions and classes, a rounding function, so by making the expression slightly more generic and moulding to the existing function’s API, I ended up with this:

*!--------------------------------------------------
function CfcRound
*!--------------------------------------------------
*! parameters
*!    tvValue             mandatory currency or numeric
*!    tnPrecision         optional, default = 0
*!    tnRoundingMethod    optional, default = 0
*!         0 = standard rounding: away from 0 at .5 or higher
*!         1 = always away from 0
*!         2 = always toward 0
*!         3 = towards 0 at .5 or LOWER
*!         Method 3 is recommended for NZ retail sales
*!  return value
*!     tvValue, rounded
	lparameters m.tvValue, m.tnPrecision, m.tnRoundingMethod
	assert Vartype(tvValue)$'NY'
	if Empty(m.tnPrecision)
		m.tnPrecision = 0
	endif
	do case
	case Empty(m.tnRoundingMethod)
		return Round(m.tvValue, m.tnPrecision)
	case m.tnRoundingMethod = 1
		return Ceiling( Abs(m.tvValue * 10^m.tnPrecision) ) / 10^m.tnPrecision * Sign(m.tvValue)
	case m.tnRoundingMethod = 2
		return floor( Abs(m.tvValue * 10^m.tnPrecision) ) / 10^m.tnPrecision * Sign(m.tvValue)
	case m.tnRoundingMethod = 3
		return floor(Ceiling( Abs(m.tvValue * 10^m.tnPrecision)-.5 )+.5) / 10^m.tnPrecision * Sign(m.tvValue)
	otherwise
		error 11 && function argument invalid
		return m.tvValue
	endcase
endfunc && CfcRound

Usage simply:

cashprice = CfcRound( exactprice, -1, 3 )

No Comments

No comments yet.

RSS feed for comments on this post. TrackBack URI

Sorry, the comment form is closed at this time.