1
\$\begingroup\$

While looking at PHP.net this afternoon, I saw a comment in round() for two functions for rounding UP and DOWN (as opposed to just HALF_UP and DOWN). I liked the suggestion, but wanted a solution that was one function instead of 3, so I came up with the following:

define('PHP_ROUND_UP',   11);
define('PHP_ROUND_DOWN', 12);

function fround($number, $precision = 0, $mode = PHP_ROUND_HALF_UP) {
    if(is_string($number)) {
        $number = preg_replace('/,/','',$number);
    }
    if (is_numeric($number) && !empty($number)) {
        switch($mode) {
            case PHP_ROUND_UP:
            case PHP_ROUND_DOWN:
                $invert = (bool) ($number < 0);
                if($invert) {
                    $number *= -1;
                }
                if (!empty($precision)) {
                    $precision = pow(10, (int) $precision);
                    $number *= $precision;
                }
                if ($mode == PHP_ROUND_UP) {
                    $number = ceil($number);
                } else {
                    $number = floor($number);
                }
                if (!empty($precision) && !empty($number)) {
                    $number /= $precision;
                }
                if($invert) {
                    $number *= -1;
                }
                break;
            default:
                $number = round($number, $precision, $mode);
                break;
        }
    }
    return (float) $number;
}

My questions are about overhead/performance, and really anything else you may have to say. For options other than PHP_ROUND_UP or PHP_ROUND_DOWN it adds minimal overhead, save for stripping commas if the input number is a string (I realize this is additional to what PHP's round() does), so I guess I am asking about the internal logic for PHP_ROUND_UP and PHP_ROUND_DOWN.

The comment that sparked this for me is here.

REVISION

\$\endgroup\$
3
  • 2
    \$\begingroup\$ Mike, if you have taken advice from the answer given to change your code, then you should post a new question with your new code. We have a policy about changing the code or the question after reviews have been given. What you may and may not do after receiving answers \$\endgroup\$ – Malachi Dec 30 '15 at 19:22
  • 1
    \$\begingroup\$ 10-4, I just did a quick browse on Meta to double-check, as posting a new CR for every revision seems like it would add a considerable amount of data to SO, but, indeed, it seems that all Meta questions on the topic are all about un-reviewed code, and the handful about reviewed code are just that: "Post again." My bad. \$\endgroup\$ – Mike Dec 30 '15 at 19:27
  • 1
    \$\begingroup\$ Side-Note: It would be nice if there was a "Post Revision" button, that created a link to the new revision, would help tying them all together, direct the crowd/conversation, etc. It would also make the requirement clearer. Maybe I will mention this on Meta. \$\endgroup\$ – Mike Dec 30 '15 at 19:29
1
\$\begingroup\$

General

Instead of having your function convert string values to numbers, throw an error if, for example !is_float($number). round() only accepts a float value, so I think you should follow their convention.

Also, this will allow you to remove is_numeric() (or, above, you could check for is_numeric() only).

This if-condition doesn't have an else, so when poor input is entered, the user could potentially not get any response at all -- terrible for debugging!

Performance

empty($number) is checked two times, you can reduce that to none since is_float() will automatically return false if there is nothing (or throw an error is there's no argument).

\$\endgroup\$
4
  • \$\begingroup\$ Yes, the preg_replace deviates from round()'s convention, and probably should not be included, but I put it in my function because it deals with some numbers from a DB, I may modify to filter outside the function first. I think is_numeric() is appropriate, as round() accepts any numeric value. As for getting "nothing," the function will return an empty (float), which follows convention 0 === round('hello'); As for $invert, this is nessesary to mimic round() behaviour with negative numbers -124 === round(-123.5) && 124 === round(123.5). I will clean up. \$\endgroup\$ – Mike Dec 30 '15 at 19:01
  • \$\begingroup\$ @Mike On second glance, is_numeric() is comparable to what's in round(), so that's fine. And $invert seems appropriate, I hadn't used round() in a while and had to go test it out! \$\endgroup\$ – Alex L Dec 30 '15 at 19:08
  • \$\begingroup\$ Updated, the use of xor is much more performant, and I LOVE getting a chance to use it when I can. \$\endgroup\$ – Mike Dec 30 '15 at 19:13
  • \$\begingroup\$ Haha very clever, if I think of anything else I'll edit my answer! \$\endgroup\$ – Alex L Dec 30 '15 at 19:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.