Skip to content

Instantly share code, notes, and snippets.

@benjaminprojas
Last active April 7, 2023 11:20
Show Gist options
  • Save benjaminprojas/4170975 to your computer and use it in GitHub Desktop.
Save benjaminprojas/4170975 to your computer and use it in GitHub Desktop.
Function to divide money (numbers with 2 decimal points) as evenly as possible between any given number of recipients
<?php
echo nl2br(print_r(divide_money_evenly((float)(string) 78.23, 3), true));
function divide_money_evenly($number, $divided_by, $numbers=array()) {
$total = 0;
for($i=1; $i<=$divided_by; $i++) {
if(abs($number - $total) != 0) {
$divided = $number / $divided_by;
if($divided < 1) {
$rounded = 0;
}
else {
$rounded = floor($divided);
}
if($rounded == 0) {
$total += 0.01;
if(isset($numbers[$i])) {
$numbers[$i] = $numbers[$i] + 0.01;
}
else {
$numbers[$i] = 0.01;
}
}
else {
$total += $rounded;
if(isset($numbers[$i])) {
$numbers[$i] = $numbers[$i] + $rounded;
}
else {
$numbers[$i] = $rounded;
}
}
}
}
$difference = $number - $total;
if($difference > 0) {
$numbers = divide_money_evenly(float)(string) $difference, $divided_by, $numbers);
}
return $numbers;
}
@sudokode
Copy link

Cool, thanks! Figured I'd post a smaller version since this got me started:

<?php

$total = 78.23;
$divisor = 3;

$total = (float) round($total, 2);

$splitTotals = array_fill(1, $divisor, round($total / $divisor, 2));

$newTotal = $splitTotals[1] * $divisor;

$difference = abs(round($newTotal - $total, 2));

$index = $divisor;
while ($difference > 0) {
    $index = $index > 0 ? $index : $divisor;

    $difference -= 0.01;

    $splitTotals[$index--] += $newTotal < $total ? 0.01 : -0.01;
}

print_r($splitTotals);

Should give the same result.

@Pikamander2
Copy link

There was a typo in the original post on line 37 causing a syntax error. Here's a corrected version:

<?php
echo nl2br(print_r(divide_money_evenly((float)(string) 78.23, 3), true));
function divide_money_evenly($number, $divided_by, $numbers=array()) {
  $total = 0;
  for($i=1; $i<=$divided_by; $i++) {
    if(abs($number - $total) != 0) {
      $divided = $number / $divided_by;
      if($divided < 1) {
        $rounded = 0;
      }
      else {
        $rounded = floor($divided);
      }
      if($rounded == 0) {
        $total += 0.01;
        if(isset($numbers[$i])) {
          $numbers[$i] = $numbers[$i] + 0.01;
        }
        else {
          $numbers[$i] = 0.01;
        }
      }
      else {
        $total += $rounded;
        if(isset($numbers[$i])) {
          $numbers[$i] = $numbers[$i] + $rounded;
        }
        else {
          $numbers[$i] = $rounded;
        }
      }
    }
  }
  $difference = $number - $total;

  if($difference > 0) {
    $numbers = divide_money_evenly((float)(string) $difference, $divided_by, $numbers);
  }
  return $numbers;
}

@Pikamander2
Copy link

Pikamander2 commented Apr 7, 2023

Here's a modified version that returns the values as an array of 2-decimal strings like '26.08' rather than 26.080000000000013.

I also cleaned up the variable names and changed its indexing to start at 0 rather than 1.

<?php
function divide_money_evenly($dollar_amount, $num_parts, $dollar_amounts=[])
{
	$total = 0;
	
	for ($i = 0; $i < $num_parts; $i++)
	{
		if (abs($dollar_amount - $total) != 0)
		{
			$divided = $dollar_amount / $num_parts;
		  
			if ($divided < 1)
			{
				$rounded = 0;
			}
			
			else
			{
				$rounded = floor($divided);
			}
			
			if ($rounded == 0)
			{
				$total += 0.01;
				
				if (isset($dollar_amounts[$i]))
				{
					$dollar_amounts[$i] = $dollar_amounts[$i] + 0.01;
				}
				
				else
				{
					$dollar_amounts[$i] = 0.01;
				}
			}
			
			else
			{
				$total += $rounded;
				
				if (isset($dollar_amounts[$i]))
				{
					$dollar_amounts[$i] = $dollar_amounts[$i] + $rounded;
				}
				
				else
				{
					$dollar_amounts[$i] = $rounded;
				}
			}
		}
	}
	
	$difference = $dollar_amount - $total;

	if ($difference > 0)
	{
		$dollar_amounts = divide_money_evenly((float)(string) $difference, $num_parts, $dollar_amounts);
	}
  
	foreach ($dollar_amounts as &$dollar_amount)
	{
	    $dollar_amount = strval($dollar_amount);
	}
  
	return $dollar_amounts;
}

Usage example:

var_export(divide_money_evenly(78.23, 3));

Expected output:

array (
  0 => '26.08',
  1 => '26.08',
  2 => '26.07',
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment