-2

TLDR: ceil(49) is resulting in 50.

I have a code that reads a CSV file from our provider for updating our database's stock and prices.

I need to get the real product cost, but our price comes in a column, and the discount applied comes into another column, so I have to calculate the original price without discount. And here comes the weird thing.

The discount comes as "0.20" for a 20% discount.

This is the code I use:

while($line = fgetcsv($file, 0, ";")) {

    $currentSku = $line[$skuPosition];
    $currentPrice = $line[$pricePosition];
    $currentDiscount = $line[$discountPosition];

    // Only ovewrite price if a discount was applied to the price
    if(floatval($currentDiscount)!=0.0 && $currentDiscount!="") {

        // Convert 0.20 to 20
        $currentDiscount*= 100;

        // Replace 45,50 to 45.50
        $currentPrice = str_replace(",", ".", $currentPrice);

        // Original price without the discount
        $currentPrice = ($currentPrice * 100) / (100 - $currentDiscount);
    }

    // Round up price
    $currentPrice = ceil($currentPrice);

    // Generate sale price
    $currentPrice = $currentPrice * $priceMultiplier;
...

We have a product that costs 39.20, and has a 20% discount applied (0.20 column value).

The price without discount is 49, and the final sale price is 49*2 = 98.

To make sure all values are correct, you make a simple calculation:

If 49 is the 100% of the price... X is the 20% to discount...

So: 20 * 49 = 980

980 / 100 = 9.8

So 9.8 is 20% of 49.

49 - 9.8 = 39.2

39.2 is the price listed on the provider's CSV.

Now, what is the final $currentPrice value of the code shown above? 100

It is calculating 50 * 2 = 100

Now I placed some debugging code to see what's going on, so here we go again: ('762.SSUBGMTVE' is the SKU of the product being proccessed)

while($line = fgetcsv($file, 0, ";")) {

    $currentSku = $line[$skuPosition];
    $currentPrice = $line[$pricePosition];
    $currentDiscount = $line[$discountPosition];

    if(floatval($currentDiscount)!=0.0 && $currentDiscount!="") {

        if($currentSku=='762.SSUBGMTVE') {

            print("1 - ".$currentDiscount."<br>");
        }

        $currentDiscount*= 100;

        if($currentSku=='762.SSUBGMTVE') {

            print("2 - ".$currentDiscount."<br>");
        }

        $currentPrice = str_replace(",", ".", $currentPrice);

        if($currentSku=='762.SSUBGMTVE') {

            print("3 - ".$currentPrice."<br>");
        }

        $currentPrice = ($currentPrice * 100) / (100 - $currentDiscount);

        if($currentSku=='762.SSUBGMTVE') {

            print("4 - ".$currentPrice."<br>");
        }
    }

    $currentPrice = ceil($currentPrice);

    if($currentSku=='762.SSUBGMTVE') {

        print("5 - ".$currentPrice."<br>");
    }

    $currentPrice = $currentPrice * $priceMultiplier;

    if($currentSku=='762.SSUBGMTVE') {

        print("6 - ".$currentPrice."<br>");
    }

This is the output:

1 - 0.200000
2 - 20
3 - 39.20
4 - 49
5 - 50
6 - 100

I also tried adding a var_dump() before point debug "5":

        if($currentSku=='762.SSUBGMTVE') {

            var_dump($currentPrice);
            print("<br>** ".ceil($currentPrice)." **<br>");
        }

        $currentPrice = ceil($currentPrice);
        if($currentSku=='762.SSUBGMTVE') {

            var_dump($currentPrice);
        }

        if($currentSku=='762.SSUBGMTVE') {

            print("5 - ".$currentPrice."<br>");
        }

And this is the output:

1 - 0.200000
2 - 20
3 - 39.20
4 - 49
float(49)
** 50 **
float(50)
5 - 50
6 - 100

And just as the final stroke, just for fun:

if($currentSku=='762.SSUBGMTVE') {

    print("4 - ".$currentPrice."<br>");
    print("Check this out!: ceil(49) results in ".ceil(49).", but ceil(".$currentPrice.") results in ".ceil($currentPrice)." ... HAHAHA!!<br>");
}

Result:

4 - 49
Check this out!: ceil(49) results in 49, but ceil(49) results in 50 ... HAHAHA!!

Could someone explain what is happening here, please?

Maybe I'm completely blinded and there's something obvious right on my nose, but right now, it doesn't matter how many times I read the code and how many tests I do, but it doesn't make any sense.

Thanks!

TheBrewer
  • 11
  • 3
  • 3
    Search for `what+every+programmer+needs+to+know+about+floating+point` – RiggsFolly Mar 17 '22 at 15:52
  • I'm sorry but I don't see how this is related (even if maybe it is). I even converted the variable to float, and it's just an integer with value 49. – TheBrewer Mar 17 '22 at 15:59
  • It's not, really. When I try it, Line 4 becomes 49.000000000000007 - https://3v4l.org/jciUG – aynber Mar 17 '22 at 16:10
  • Please read warning part of this page: https://www.php.net/manual/en/language.types.float.php – keyhan Mar 17 '22 at 16:13
  • FYI, you are unnecessarily complicating your math expression and a simplified version tested in virtually every version of PHP available on https://sandbox.onlinephpfunctions.com/ does not have this problem (at least with the given values). First, do not multiply anything by `100`, then use: ` $originalPrice = $currentPrice / (1 - $currentDiscount); // float(49)` – Arleigh Hix Mar 17 '22 at 20:29

1 Answers1

0

Well.... https://imgs.xkcd.com/comics/rounding.png

But in all seriousness, try this:

        $currentPrice = round(($currentPrice * 100) / (100 - $currentDiscount), 2);

You should end up with: (39.20*100)/(100-20) = 49.00

If you don't, then try rounding round($currentPrice, 2) and see what you get after in the decimal places.

JorgeObregon
  • 3,020
  • 1
  • 12
  • 12
  • Thanks for the idea, the first round() did the trick. Also, I also tested what aynber did, and in PHP 8 the var_dump() returns indeed 49.00000000000001, instead of 49 like in PHP 7. I knew that numbers like 10.00002 round to 11, but all my debugs showed "49", and that's what made me crazy. PS: Tried to upvote your answer, but apparently I'm not worth it yet. Sorry. – TheBrewer Mar 17 '22 at 17:29