At work I quite often have to normalise prices before they're displayed on the site (I work for an online shop). So, for example, if the price comes up as £12.34 and we want things to be rounded up to the nearest 49 or 99 pence then the actual rendered price should be £12.49. Below is the function I wrote for this, it works but it feels like it could be more gracefull. Any thoughts?
/**
* Normalise the price provided towards the given targets
* A positive target implies the price should be rounded up
* A negative target implies it should be rounded down
*
* @param float $price
* @param float $target...
* @return float
*/
function normalise_price( $price, $target )
{
// get the price fraction
$fraction = $price - intval( $price );
// to store the closest target so far
$closest = array();
// get all the targets, multiple can be passed in
$targets = array_slice( func_get_args(), 1 );
foreach( $targets as $target ) {
if( $target > 0 ) {
// rounding up
if( $target < $small ) {
// crossing the £1 barrier
$diff = $small + (1-$target);
} else {
$diff = $target - $small;
}
} else {
// rounding down
if( abs( $target ) > $small ) {
$diff = $small + 1 + $target;
} else {
$diff = $small + $target;
}
}
if( $diff < $closest['diff'] or !isset( $closest['diff'] ) ) {
$closest['diff'] = $diff;
$closest['target'] = $target;
}
}
if( $closest['target'] > 0 ) {
// rounding up
if($small > $closest['target']) {
return $price + ( 1 - $closest['diff'] );
} else {
return $price + $closest['diff'];
}
} else {
// rounding down
if($small < abs($closest['target'])) {
return $price - $closest['diff'];
} else {
return $price - $closest['diff'];
}
}
}
It is used like this
$prices = array(
12.34,
24.63,
19.74,
12.92,
20.25
);
foreach( $prices as $price ) {
print $price . "=>";
$price_n = normalise_price( $price, 0.45, 0.99 );
print $price_n . "\n\n";
}
... which prints ...
12.34=>12.45
24.63=>24.99
19.74=>19.99
12.92=>12.99
20.25=>20.45
cheers
rob