Je voudrais attacher une valeur x
à une plage [a, b]
:
x = (x < a) ? a : ((x > b) ? b : x);
C'est assez basique. Mais je ne vois pas de fonction "clamp" dans la bibliothèque de classes - du moins pas dans System.Math
.
(Pour l’ignorant, "verrouiller" une valeur consiste à s’assurer qu’elle se situe entre certaines valeurs maximales et minimales. Si elle est supérieure à la valeur maximale, elle est remplacée par la valeur maximale, etc.)
Vous pouvez écrire une méthode d'extension:
public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
{
if (val.CompareTo(min) < 0) return min;
else if(val.CompareTo(max) > 0) return max;
else return val;
}
EDIT: les méthodes d’extension vont dans des classes statiques - comme il s’agit d’une fonction de bas niveau, elle devrait probablement aller dans un espace de noms principal de votre projet. Vous pouvez ensuite utiliser la méthode dans n’importe quel fichier de code contenant une directive using pour l’espace de nommage, par exemple.
using Core.ExtensionMethods
int i = 4.Clamp(1, 3);
À partir de .NET Core 2.0, System.Math
a maintenant une méthode Clamp
qui peut être utilisée à la place:
using System;
int i = Math.Clamp(4, 1, 3);
Essayer:
public static int Clamp(int value, int min, int max)
{
return (value < min) ? min : (value > max) ? max : value;
}
Il suffit d'utiliser Math.Min
et Math.Max
:
x = Math.Min(Math.Max(x, a), b);
Il n'y en a pas, mais ce n'est pas trop difficile d'en faire un. J'en ai trouvé un ici: clamp
Il est:
public static T Clamp<T>(T value, T max, T min)
where T : System.IComparable<T> {
T result = value;
if (value.CompareTo(max) > 0)
result = max;
if (value.CompareTo(min) < 0)
result = min;
return result;
}
Et il peut être utilisé comme:
int i = Clamp(12, 10, 0); -> i == 10
double d = Clamp(4.5, 10.0, 0.0); -> d == 4.5
Il n'y en a pas dans l'espace de noms System.Math
http://msdn.Microsoft.com/en-us/library/system.math_members.aspx
Il existe une classe MathHelper où elle est disponible pour le studio de jeu XNA si c'est ce que vous faites:
http://msdn.Microsoft.com/en-us/library/bb197892(v=XNAGameStudio.31).aspx
Just sharing La solution de Lee avec les problèmes, commentaires et préoccupations traités, dans la mesure du possible:
public static T Clamped<T>(this T value, T min, T max) where T : IComparable<T> {
if (value == null) throw new ArgumentNullException(nameof(value), "is null.");
if (min == null) throw new ArgumentNullException(nameof(min), "is null.");
if (max == null) throw new ArgumentNullException(nameof(max), "is null.");
//If min <= max, clamp
if (min.CompareTo(max) <= 0) return value.CompareTo(min) < 0 ? min : value.CompareTo(max) > 0 ? max : value;
//If min > max, clamp on swapped min and max
return value.CompareTo(max) < 0 ? max : value.CompareTo(min) > 0 ? min : value;
}
Différences:
ed
) pour indiquer (en outre) que la valeur n'est pas bloquée sur place et qu'une nouvelle valeur est renvoyée (voir @ commentaire de JimBalter ).null check
est approprié sur toutes les entrées (voir @ commentaire de JeppeStigNielsen ).min
et max
si min > max
(voir @ commentaire de JeppeStigNielsen ).Limitations: Pas de pinces unilatérales. Si max
est NaN
, retourne toujours NaN
(voir commentaire de Herman ).
Le code ci-dessous prend en charge la spécification de bornes dans n’importe quel ordre (par exemple bound1 <= bound2
ou bound2 <= bound1
). J'ai trouvé cela utile pour le serrage de valeurs calculées à partir d'équations linéaires (y=mx+b
) où la pente de la ligne peut augmenter ou diminuer.
Je sais: le code se compose de cinq opérateurs d’expression conditionnelle super-laids . La chose est, cela fonctionne , et les tests ci-dessous le prouvent. N'hésitez pas à ajouter des parenthèses strictement inutiles si vous le souhaitez.
Vous pouvez facilement créer d'autres surcharges pour d'autres types numériques et simplement copier/coller les tests.
Avertissement: Comparer les nombres en virgule flottante n'est pas simple. Ce code n'implémente pas les comparaisons double
avec robustesse. Utilisez une bibliothèque de comparaison en virgule flottante pour remplacer les utilisations des opérateurs de comparaison.
public static class MathExtensions
{
public static double Clamp(this double value, double bound1, double bound2)
{
return bound1 <= bound2 ? value <= bound1 ? bound1 : value >= bound2 ? bound2 : value : value <= bound2 ? bound2 : value >= bound1 ? bound1 : value;
}
}
tests xUnit/FluentAssertions:
public class MathExtensionsTests
{
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(0, 0, 2, 0)]
[InlineData(-1, 0, 2, 0)]
[InlineData(1, 0, 2, 1)]
[InlineData(2, 0, 2, 2)]
[InlineData(3, 0, 2, 2)]
[InlineData(0, 2, 0, 0)]
[InlineData(-1, 2, 0, 0)]
[InlineData(1, 2, 0, 1)]
[InlineData(2, 2, 0, 2)]
[InlineData(3, 2, 0, 2)]
public void MustClamp(double value, double bound1, double bound2, double expectedValue)
{
value.Clamp(bound1, bound2).Should().Be(expectedValue);
}
}
En utilisant les réponses précédentes, je l'ai résumé au code ci-dessous pour mes besoins. Cela vous permettra également de fixer un nombre uniquement par son minimum ou maximum.
public static class IComparableExtensions
{
public static T Clamped<T>(this T value, T min, T max)
where T : IComparable<T>
{
return value.CompareTo(min) < 0 ? min : value.ClampedMaximum(max);
}
public static T ClampedMinimum<T>(this T value, T min)
where T : IComparable<T>
{
return value.CompareTo(min) < 0 ? min : value;
}
public static T ClampedMaximum<T>(this T value, T max)
where T : IComparable<T>
{
return value.CompareTo(max) > 0 ? max : value;
}
}