La forma más eficiente/elegante de cortar un número?


Dado un real (n), un valor máximo este real puede ser (superior), y un valor mínimo este real puede ser (inferior), ¿cómo podemos recortar más eficientemente n, de modo que permanezca entre inferior y superior?

Por supuesto, el uso de un montón de declaraciones if puede hacer esto, pero eso es aburrido! ¿Qué pasa con soluciones más compactas y elegantes/divertidas?

Mi propio intento rápido (C/C++):

float clip( float n, float lower, float upper )
{
    n = ( n > lower ) * n + !( n > lower ) * lower;
    return ( n < upper ) * n + !( n < upper ) * upper;
}

Estoy seguro de que hay otras, mejores maneras de hacer esto, es por eso que estoy poniendo esto por ahí..!

Author: Alex Z, 2012-02-17

12 answers

¿Qué pasa con aburrido, viejo, legible, y más corto todavía:

float clip(float n, float lower, float upper) {
  return std::max(lower, std::min(n, upper));
}

?

Esta expresión también podría ser 'genérica' de la siguiente manera:

template <typename T>
T clip(const T& n, const T& lower, const T& upper) {
  return std::max(lower, std::min(n, upper));
}

Update

Billy ONeal agregó:

Tenga en cuenta que en Windows es posible que tenga que definir NOMINMAX porque definen macros min y max que entran en conflicto

 69
Author: justin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-01 03:22:00

¿Por qué reescribir algo que ya ha sido escrito para ti?

#include <boost/algorithm/clamp.hpp>
boost::algorithm::clamp(n, lower, upper);

A partir de C++17, esto es ahora parte del STL :

#include <algorithm>
std::clamp(n, lower, upper);
 26
Author: Riot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-28 11:01:39

Se espera que C++17 agregue una función clamp. Cortesía de cppreference.com:

template<class T>
constexpr const T& clamp( const T& v, const T& lo, const T& hi );

template<class T, class Compare>
constexpr const T& clamp( const T& v, const T& lo, const T& hi, Compare comp );
 18
Author: Josh Kelley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-16 20:42:54

Rara vez iría más allá...

return n <= lower ? lower : n >= upper ? upper : n;

Si sabe que puede tenerlos, querrá verificar si NaN / Inf, etc. se conservan....

Digo rara vez y no nunca solo porque a veces menos ramificación puede ser más rápido, pero seguro que desea perfilarlo y demostrar que ayudó y importaba....

 14
Author: Tony Delroy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-20 04:48:13

Puede que te guste el operador ternario:

value = value<lower?lower:value;
value = value>upper?upper:value;
 5
Author: Rich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-17 06:47:17

Poco elegante, inseguro, costoso pero sin ramificaciones:

n= 0.5 * (n + lower + fabs(n - lower));
n= 0.5 * (n + upper - fabs(upper - n));
 4
Author: Yves Daoust,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-17 09:58:29

La función tangente hiperbólica hace precisamente eso de una manera bastante elegante (se usa mucho para redes neuronales). Véase el código a continuación.

Tanh función de hacha en 0

float clip(float x, float min, float max) {
  return ((max-min)/2)*((exp(x) - exp(-x))/(exp(x) + exp(-x))) + max - (max-min)/2;
}
 2
Author: HichameMoriceau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-10 04:59:02
n = n + ((n < lower) * (lower - n)) + ((n > upper) * (upper - n));
 1
Author: user997112,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-28 11:36:42

Si el rendimiento realmente le importa, ¿qué pasa con una solución en línea que evita la asignación cuando no se requiere:

#define clip(n, lower, upper) if (n < lower) n= lower; else if (n > upper) n= upper
 0
Author: Yves Daoust,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-17 09:35:13

El siguiente archivo de cabecera debería funcionar para C y C++. Tenga en cuenta que desactiva min y max si las macros ya están definidas:

#pragma once

#ifdef min
#undef min
#endif

#ifdef max
#undef max
#endif

#ifdef __cplusplus
#include <algorithm>

template <typename T>
T clip(T in, T low, T high)
{
    return std::min(std::max(in, low), high);
}
#else /* !__cplusplus */
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) < (b)) ? (b) : (a))
#define clip(a, b, c) min(max((a), (b)), (c))
#endif /* __cplusplus */
 0
Author: Woody,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-10 19:41:46

Lo mejor es claramente

template <typename t>
t clamp2(t x, t min, t max)
{
if (x < min) x = min;
if (x > max) x = max;
return x;
}

Como se compila a

movss   xmm0, cs:__real@c2c80000
maxss   xmm0, [rsp+38h+var_18]
movss   xmm1, cs:__real@42c80000
minss   xmm1, xmm0
movss   [rsp+38h+var_18], xmm1

Tiene 0 ramas y debe ser la más rápida de todas las publicadas anteriormente.

También msvc141 con la configuración de versión estándar

 0
Author: nsn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-22 16:08:51

Si desea usar xtensor, soportaría arreglos multidimensionales y la solución sería muy elegante.

#include <iostream>
#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"
#include "xtensor/xview.hpp"
#include "xtensor/xrandom.hpp"
xt::xarray<float> ar({2.1, 2.9, -2.1, -2.9});
std::cout<<xt::cast<int>(xt::trunc(ar))<<std::endl;

//la Respuesta es { 2, 2, -2, -2 }

 0
Author: Achyut Sarma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-12 18:43:28