Skip to content

Instantly share code, notes, and snippets.

@Potherca
Last active February 23, 2022 12:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Potherca/55ca0e9377bbd49ee41241380e1fe3f9 to your computer and use it in GitHub Desktop.
Save Potherca/55ca0e9377bbd49ee41241380e1fe3f9 to your computer and use it in GitHub Desktop.
Comparison of various styles of writing a `clamp` function in PHP

Introduction

PHP does not have a native "clamp" (or range constraint) function(1).

As always, there are several ways to resolve this problem.

This repo contains several variations that all achieve the same thing: clamp a given value between a given maximum and minimum range.(2)

The question is: which one has your preference and why?

It is important to note that there is no right or wrong answer, all of the implementations are fully functional. the goal is not to see which variation is more popular but to gain an understanding of why a developer prefers one over the other.

Concerns

When judging code, developers tend to look at the following things(3):

  • Amount of code
  • Code complexity
  • Execution speed
  • Fault tolerance (being bug free)
  • Readability

Of these, only "Readability" is subjective. Complexity, fault tolerance, speed and size can all be measured.

Scope

To reduce the feature scope to the absolute minimum, the code in the functions is assumed to be low-level code. Validation, type coersion, etc are considered out of scope for the sake of this discussion.

At the point clamp is called:

  • $value, $min and $max are all assumed to be integers
  • $min is assumed to be smaller than $max

Footnotes

  1. Although one is proposed for PHP8+
  2. To avoid the order of the files causing a bias, the filenames contain a partial MD5 hash of their content.
  3. Let me know if I missed anything
<?php
function clamp($value, $min, $max)
{
return $value < $min ? $min : ( $value > $max ? $max : $value );
}
<?php
function clamp($value, $min, $max)
{
if ($value < $min) {
$value = $min;
}
if ($value > $max) {
$value = $max;
}
return $value;
}
<?php
function clamp($value, $min, $max)
{
return min($max, max($min, $value));
}
<?php
function clamp($value, $min, $max)
{
if ($value < $min) {
$value = $min;
} elseif ($value > $max) {
$value = $max;
}
return $value;
}
<?php
function clamp($value, $min, $max)
{
if ($value < $min) {
return $min;
}
if ($value > $max) {
return $max;
}
return $value;
}
<?php
function clamp($value, $min, $max)
{
if ($value < $min) return $min;
if ($value > $max) return $max;
return $value;
}
<?php
function clamp($value, $min, $max)
{
if ($value < $min) {
return $min;
} elseif ($value > $max) {
return $max;
} else {
return $value;
}
}
@jpoorthuis
Copy link

jpoorthuis commented Sep 15, 2017

I'd say number 4 (clamp.83C742FF.php), for readability's sake and executes the least amount of code.
Min/max you have to read carefully what's happening, and does two extra function calls.

@Ocramius
Copy link

Ocramius commented Sep 15, 2017

clamp.832b9c44.php for me: intent is cleaner, and has a simpler phi representation, which means easier to optimise for future compiler versions

@mjrider
Copy link

mjrider commented Sep 15, 2017

Min/max maybe written in 3 lines, no ifelse needed so less code paths to follow mentally

@Potherca
Copy link
Author

Potherca commented Sep 15, 2017

@mjrider Good point! I will add the variation. Could you show me how you'd write it?

@jslegers
Copy link

jslegers commented Sep 15, 2017

I'd like to nominate yet another alternative... namely one that uses ternary operators :

function clamp($value, $min, $max) {
    return $value < $min ? $min : ( $value > $max ? $max : $value );
}

I like this option because I love oneliners, and because using ternary operators is much faster than going for min($max, max($min, $value));.

Here's the benchmark I used to compare performance :

function clamp1($value, $min, $max) {
    return $value < $min ? $min : ( $value > $max ? $max : $value );
}

function clamp2($value, $min, $max) {
    if ($value < $min) {
        $value = $min;
    } elseif ($value > $max) {
        $value = $max;
    }      
    return $value;
}

function clamp3($value, $min, $max) {
    if ($value < $min) return $min;
    if ($value > $max) return $max;
    return $value;
}

function clamp4($value, $min, $max) {
    return min($max, max($min, $value));
}

$start_time = microtime(TRUE);

for($i = 0; $i < 10000000; $i++) {
    clamp1 (18, 12, 25); // Replace function and/or parameters and rerun the benchmark to compare
}

$end_time = microtime(TRUE);

echo $end_time - $start_time;

Still, my preference goes to the following option :

function clamp($value, $min, $max) {
    if ($value < $min) return $min;
    if ($value > $max) return $max;
    return $value;
}

It's not a oneliner, but it's still pretty compact, it's very readable and it's also very fast!

And I think that's the threeliner @mjrider was talking about.

@Potherca
Copy link
Author

@jpoorthuis I am interested in how you define executes the least amount of code. What are we counting here?
In regards to Min/max [...] does two extra function calls, what is your concern with that?

@Potherca
Copy link
Author

Potherca commented Sep 15, 2017

@jslegers Thanks for responding!

Both of your suggestions have been added.

I've also added some benchmarks.

@jslegers
Copy link

jslegers commented Sep 19, 2017

@Potherca

It looks like your benchmarks produce results very different from my own.

As your results are probably more accurate than my quick-and-dirty tests, I'd like to change my answer to 832b9c44, considering it's the option with both the least code ánd the one with the best performance!

@mjrider
Copy link

mjrider commented Nov 3, 2017

just for the record, i was refering to 832B9C44

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