Skip to content

Instantly share code, notes, and snippets.

@gh640
Last active December 19, 2020 20:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gh640/6d65226c6203f2cb0ebe42fbddca8ece to your computer and use it in GitHub Desktop.
Save gh640/6d65226c6203f2cb0ebe42fbddca8ece to your computer and use it in GitHub Desktop.
PHP: ceil() and floor() with precision
<?php
function ceil_plus(float $value, ?int $precision = null): float
{
if (null === $precision) {
return (float) ceil($value);
}
if ($precision < 0) {
throw new \RuntimeException('Invalid precision');
}
$reg = $value + 0.5 / (10 ** $precision);
return round($reg, $precision, $reg > 0 ? PHP_ROUND_HALF_DOWN : PHP_ROUND_HALF_UP);
}
function floor_plus(float $value, ?int $precision = null): float
{
if (null === $precision) {
return (float)floor($value);
}
if ($precision < 0) {
throw new \RuntimeException('Invalid precision');
}
$reg = $value - 0.5 / (10 ** $precision);
return round($reg, $precision, $reg > 0 ? PHP_ROUND_HALF_UP : PHP_ROUND_HALF_DOWN);
}
// Tests for ceil_plus().
assert(ceil_plus(3.14, 0) === 4.0);
assert(ceil_plus(3.14, 1) === 3.2);
assert(ceil_plus(3.14, 2) === 3.14);
assert(ceil_plus(3.1415, 0) === 4.0);
assert(ceil_plus(3.1415, 1) === 3.2);
assert(ceil_plus(3.1415, 2) === 3.15);
assert(ceil_plus(3.1415, 3) === 3.142);
assert(ceil_plus(3.1415, 4) === 3.1415);
assert(ceil_plus(0.002, 3) === 0.002);
assert(ceil_plus(0.0015, 3) === 0.002);
assert(ceil_plus(0.001, 3) === 0.001);
assert(ceil_plus(0, 0) === 0.0);
assert(ceil_plus(-0.001, 3) === -0.001);
assert(ceil_plus(-0.0015, 3) === -0.001);
assert(ceil_plus(-0.002, 3) === -0.002);
assert(ceil_plus(-1, 0) === -1.0);
// Tests for floor_plus().
assert(floor_plus(3.14, 0) === 3.0);
assert(floor_plus(3.14, 1) === 3.1);
assert(floor_plus(3.14, 2) === 3.14);
assert(floor_plus(3.1415, 0) === 3.0);
assert(floor_plus(3.1415, 1) === 3.1);
assert(floor_plus(3.1415, 2) === 3.14);
assert(floor_plus(3.1415, 3) === 3.141);
assert(floor_plus(3.1415, 4) === 3.1415);
assert(floor_plus(0.002, 3) === 0.002);
assert(floor_plus(0.0015, 3) === 0.001);
assert(floor_plus(0.001, 3) === 0.001);
assert(floor_plus(0, 0) === 0.0);
assert(floor_plus(-0.001, 3) === -0.001);
assert(floor_plus(-0.0015, 3) === -0.002);
assert(floor_plus(-0.002, 3) === -0.002);
assert(floor_plus(-1, 0) === -1.0);
@malsatin
Copy link

hi there. The function floor_plus would work incorrectly for 0 and 1 values

@gh640
Copy link
Author

gh640 commented Dec 11, 2020

@malsatin Hi. Thank you for your comment. I tested some patterns.

0:

var_dump(floor_plus(0, 0));  # => float(-1). This should be float(0).
var_dump(floor_plus(0, 1));  # => float(-0.1). This should be float(0).

1:

var_dump(floor_plus(1, 0));  # => float(1) 
var_dump(floor_plus(1, 1));  # => float(1)

I confirmed the returned values for 0 are wrong. But the returned values for 1 look correct. Is my understanding correct?

@malsatin
Copy link

@gh640
Sorry, was a bit tired. Actually it works fine for 1. What I have would yesterday is that if works incorrectly for 0.001

var_dump(floor_plus(0.002, 3)); // => float(0.002)
var_dump(floor_plus(0.001, 3)); // => float(0)

@malsatin
Copy link

Yesterday I have also make a working solution, with some help of StackOverflow tips. Now it also supports negative values rounding.

ceil

function ceil(float $value, ?int $precision = null): float
{
    if (null === $precision) {
        return (float)ceil($value);
    }
    if ($precision < 0) {
        throw new \RuntimeException('Invalid precision');
    }

    $reg = $value + 0.5 / (10 ** $precision);

    return round($reg, $precision, $reg > 0 ? PHP_ROUND_HALF_DOWN : PHP_ROUND_HALF_UP);
}

ceil:

function floor(float $value, ?int $precision = null): float
{
    if (null === $precision) {
        return (float)floor($value);
    }
    if ($precision < 0) {
        throw new \RuntimeException('Invalid precision');
    }

    $reg = $value - 0.5 / (10 ** $precision);

    return round($reg, $precision, $reg > 0 ? PHP_ROUND_HALF_UP : PHP_ROUND_HALF_DOWN);
}

@gh640
Copy link
Author

gh640 commented Dec 17, 2020

@malsatin Thank you for your comments. Your code looks better. I'd like to update the code but I'm busy and don't have enough time now... :( I'll come back soon. Thanks!

@gh640
Copy link
Author

gh640 commented Dec 19, 2020

@malsatin Sorry for my slow reaction. I tested some cases with you implementation and they look fine! I've updated the code above with some additional assertions. Thank you.

@malsatin
Copy link

Thank you too. Hopefully this thread would help someone in the future

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