1

I am trying to make an algorithm that will round according to these conditions:

  • 1 or 2 cents - rounded down to "0"
  • 3 or 4 cents - rounds up to 5 cents
  • 5 cents - 5 cents left
  • 6 or 7 cents - rounds down to 5 cents
  • 8 or 9 cents - rounds up to "0"

So, it should look like this:

PRICE| AFTER
9.90 | 9.90
9.91 | 9.90
9.92 | 9.90
9.93 | 9.95
9.94 | 9.95
9.95 | 9.95
9.96 | 9.95
9.97 | 9.95
9.98 | 10.0
9.99 | 10.0
10.0 | 10.0

My code is here, but looks strange. For numbers like 10.99 result becomes 10.10:

 function roundDown(float $price): float
    {
        [$number, $decimals] = explode('.', sprintf('%.2f', $price));
        [, $second] = str_split($decimals);

        $table = [
            0 => 0,
            1 => -1,
            2 => -2,
            3 => 2,
            4 => 1,
            5 => 0,
            6 => -1,
            7 => -2,
            8 => 2,
            9 => 1,
        ];

        return $number . '.' . str_pad((float)$decimals+(float)$table[$second], 2, '0', STR_PAD_LEFT);
    }

Thanks in advance.

5
  • 4
    What have you tried so far? Commented Aug 15, 2022 at 16:29
  • Sorry, updated. Commented Aug 15, 2022 at 17:12
  • 1
    What do you mean "Not working for numbers without decimals?" You are rounding more-or-less to the nearest .05. So feeding a whole number would just spit back the whole number, if your code is working as your requirements describe. Can you share what your expected output of a number-without-decimal would be? Commented Aug 15, 2022 at 17:12
  • 1
    Q: Would Richard Dobroň's solution work for you? If "Yes", please "Upvote" (triangle icon) and "Accept" his answer. Q: What about values like 9.50? 9.47? 9.52? Commented Aug 15, 2022 at 17:47
  • Richard's solution is exactly what I was looking for, thanks for the help guys! Commented Aug 15, 2022 at 18:05

2 Answers 2

1

You have forgotten to carry the hundreds when you reach 100: 1.99 + 0.01 needs to become 2.00

Sticking to your current approach, you could check if the new "decimals" reach 100 or higher after your adj, and if so increment the "number":

function roundDown(float $price): float
{
    [$number, $decimals] = explode('.', sprintf('%.2f', $price));
    [, $second] = str_split($decimals);

    $table = [
        0 => 0,
        1 => -1,
        2 => -2,
        3 => 2,
        4 => 1,
        5 => 0,
        6 => -1,
        7 => -2,
        8 => 2,
        9 => 1,
    ];

    $newDecimals = (float)$decimals+(float)$table[$second];
    if ( $newDecimals > 99 ) {
        $number += 1;
        $newDecimals -= 100;
    }
    return $number . '.' . str_pad($newDecimals, 2, '0', STR_PAD_LEFT);
}

(There are various other ways to improve the code, or implement the requirement a different way, but I wanted to point out the specific bug in your current attempt, and the simplest change that fixes it.)

Sign up to request clarification or add additional context in comments.

Comments

0

I would avoid the string manipulation at the end and set the values in $table to be the actuals values to be added or subtracted and then do the arithmetic at the end.

function roundDown(float $price): float
{
    [$number, $decimals] = explode('.', sprintf('%.2f', $price));
    [, $second] = str_split($decimals);

    $table = [
        0 => 0,
        1 => -0.01,
        2 => -0.02,
        3 => 0.02,
        4 => 0.01,
        5 => 0,
        6 => -0.01,
        7 => -0.02,
        8 => 0.02,
        9 => 0.01,
    ];

    return $price + $table[$second];
}

2 Comments

You're correct: I would definitely avoid string manipulation :)
This feels a bit half-way between two approaches: you're still doing string manipulation rather than arithmetic to choose the increment.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.