it-swarm.com.de

Die effizienteste/eleganteste Art, eine Nummer zu schneiden?

Wenn ein Real (n) gegeben ist, kann ein Maximalwert (Real) dieser Real sein (Oberer Wert) und ein Minimalwert, den dieser Real (Niedrig) sein kann. Wie können wir n am effizientesten schneiden, so dass er zwischen Unter und Oberer Ebene bleibt?

Natürlich kann eine Reihe von if-Anweisungen dies tun, aber das ist langweilig! Was ist mit kompakteren und eleganteren/lustigsten Lösungen?

Mein eigener schneller Versuch (C/C++):

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

Ich bin sicher, es gibt andere, bessere Möglichkeiten, dies zu tun, deshalb setze ich das hier raus ...!

41
Alex Z

Was ist langweilig, alt, lesbar und noch kürzer:

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

?

Dieser Ausdruck könnte auch wie folgt "generisiert" werden:

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 fügte hinzu:

Beachten Sie, dass Sie unter Windows möglicherweise NOMINMAX definieren müssen, da hier Min- und Max-Makros definiert werden, bei denen Konflikte auftreten

71
justin

Warum etwas umschreiben, was bereits für Sie geschrieben wurde ?

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

Seit C++ 17 ist dies jetzt Teil der STL :

#include <algorithm>
std::clamp(n, lower, upper);
35
Riot

Es wird erwartet, dass C++ 17 eine clamp - Funktion hinzufügt. Mit freundlicher Genehmigung von 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
Josh Kelley

Ich würde sehr selten darüber hinausgehen ...

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

Wenn Sie wissen, dass Sie sie haben, sollten Sie prüfen, ob NaN/Inf usw. erhalten bleiben.

Ich sage selten und nicht nur deshalb, weil manchmal weniger Verzweigungen schneller sein können, aber Sie möchten es sicher profilieren und beweisen, dass es geholfen hat und wichtig war ....

14
Tony Delroy

Ihnen könnte der ternäre Operator gefallen:

value = value<lower?lower:value;
value = value>upper?upper:value;
5
Rich

Unelegant, unsicher, kostspielig aber verzweifelt:

n= 0.5 * (n + lower + fabs(n - lower));
n= 0.5 * (n + upper - fabs(upper - n));
4
Yves Daoust

Die hyperbolische Tangensfunktion tut dies auf recht elegante Weise (häufig für neuronale Netzwerke verwendet). Siehe Code unten.

 Tanh function axed on 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
HichameMoriceau

das beste ist eindeutig

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

wie es zu kompilieren

movss   xmm0, cs:[email protected]
maxss   xmm0, [rsp+38h+var_18]
movss   xmm1, cs:[email protected]
minss   xmm1, xmm0
movss   [rsp+38h+var_18], xmm1

es hat 0 Äste und sollte die schnellste aller oben genannten sein.

auch msvc141 mit den Standard-Release-Einstellungen

1
nsn
n = n + ((n < lower) * (lower - n)) + ((n > upper) * (upper - n));
1
user997112

Eine Warnung an alle, die versuchen, so etwas zu tun, wobei Sie die möglichen Werte des Eingabetyps festlegen.

template<typename T>
T clamp(T input)
{
    return boost::algorithm::clamp(input, 
                                   std::numeric_limits<T>::min(),
                                   std::numeric_limits<T>::max());
}

Dies schlägt für einige Werte von T und input fehl. Zum Beispiel:

clamp<int16_t>(32768.0);

wird zurückkehren

-32767

Probieren Sie es aus und sehen Sie. Das Problem ist, dass input beim Eintritt in die Funktion auf T umgewandelt wird, bevor die Vergleiche durchgeführt werden. Und wenn Sie static_cast<int16_t>(+32768), erhalten Sie UB.

Ich habe keine gute Lösung außer dem Code, der "besser", aber nicht vollständig ist. Dies funktioniert für kleine Integer-Typen (int16_t und int32_t) und mit einfacher Genauigkeit float, hat jedoch Probleme mit int64_t und double.

template<typename T>
T clamp(double input)
{
    double intermediate = return boost::algorithm::clamp<double>(input, 
                        std::numeric_limits<T>::min(),
                        std::numeric_limits<T>::max());
    return boost::numeric_cast<T>(intermediate);
}
1
Mark Lakata

Wenn Leistung für Sie wirklich wichtig ist, wie sieht es mit einer Inline-Lösung aus, die die Zuordnung vermeidet, wenn sie nicht benötigt wird:

#define clip(n, lower, upper) if (n < lower) n= lower; else if (n > upper) n= upper
0
Yves Daoust

Wenn Sie xtensor verwenden möchten, werden mehrdimensionale Arrays unterstützt, und die Lösung wäre sehr elegant.

#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;

// Antwort ist {2, 2, -2, -2}

0
Achyut Sarma

Die folgende Header-Datei sollte für C und C++ funktionieren. Beachten Sie, dass min und max nicht definiert werden, wenn die Makros bereits definiert sind:

#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
Woody