Eu gostaria de fixar um valor x
em um intervalo [a, b]
:
x = (x < a) ? a : ((x > b) ? b : x);
Isso é bastante básico. Mas não vejo uma função "clamp" na biblioteca de classes - pelo menos não em System.Math
.
(Para quem não sabe, "fixar" um valor é certificar-se de que ele está entre alguns valores máximo e mínimo. Se for maior que o valor máximo, ele será substituído pelo máximo, etc.)
Respostas:
Você pode escrever um método de extensão:
Os métodos de extensão vão em classes estáticas - uma vez que esta é uma função de baixo nível, provavelmente deve ir em algum namespace central em seu projeto. Você pode então usar o método em qualquer arquivo de código que contenha uma diretiva de uso para o namespace, por exemplo
.NET Core 2.0
A partir do .NET Core 2.0
System.Math
agora existe umClamp
método que pode ser usado:fonte
IComparable
é que não ocorre boxe. Isso deve ser executado muito rápido. Lembre-se de que comdouble
efloat
, oCompareTo
método corresponde a uma ordem total ondeNaN
é menor que todos os outros valores, inclusiveNegativeInfinity
. Portanto, não é equivalente ao<
operador. Se você usou<
com um tipo de ponto flutuante, você teria que considerar como tratarNaN
também. Isso não é relevante para outros tipos numéricos.NaN
em ambos os casos. A versão com<
e>
faria de saídaNaN
e usandoNaN
paramin
oumax
seria efetivamente fazer um one-sided grampo. ComCompareTo
ele voltaria sempreNaN
semax
forNaN
.Basta usar
Math.Min
eMath.Max
:fonte
int a0 = x > a ? x : a; return a0 < b ? a0 : b
que (embora forneça resultados corretos) não é exatamente ideal.Math.Min(Math.Max(x, min), max)
resulta em mais uma comparação do que o necessário se x <min.Experimentar:
fonte
Não existe um, mas não é muito difícil fazer um. Encontrei um aqui: braçadeira
Isto é:
E pode ser usado como:
fonte
Clamp(T value, T min, T max)
Não há nenhum no
System.Math
namespace .Há uma
MathHelper
classe disponível para o estúdio de jogos XNA se for isso o que você está fazendo:fonte
Apenas compartilhando a solução de Lee com os problemas e preocupações dos comentários tratados, sempre que possível:
Diferenças:
ed
) para (mais) indicar que o valor não está preso no local e que, em vez disso, um novo valor é retornado (consulte o comentário de @JimBalter ).null check
em todas as entradas (veja o comentário de @JeppeStigNielsen ).min
emax
ifmin > max
(veja o comentário de @JeppeStigNielsen ).Limitações: Sem grampos unilaterais. Se
max
forNaN
, sempre retornaNaN
(veja o comentário de Herman ).fonte
nameof
que não funciona para C # 5 ou inferior.Usando as respostas anteriores, eu condensava no código abaixo para minhas necessidades. Isso também permitirá que você fixe um número apenas por seu mínimo ou máximo.
fonte
return value.ClampedMinimum(min).ClampedMaximum(max);
?O código a seguir suporta a especificação de limites em qualquer ordem (ou seja
bound1 <= bound2
, oubound2 <= bound1
). Eu achei isso útil para fixar valores calculados a partir de equações lineares (y=mx+b
), onde a inclinação da linha pode ser crescente ou decrescente.Eu sei: o código consiste em cinco operadores de expressão condicional extremamente feios . A questão é que funciona e os testes abaixo provam isso. Sinta-se à vontade para adicionar parênteses estritamente desnecessários, se desejar.
Você pode criar facilmente outras sobrecargas para outros tipos numéricos e basicamente copiar / colar os testes.
Aviso: comparar números de ponto flutuante não é simples. Este código não implementa
double
comparações de forma robusta. Use uma biblioteca de comparação de ponto flutuante para substituir os usos de operadores de comparação.Testes xUnit / FluentAssertions:
fonte
Se eu quiser validar o intervalo de um argumento em [min, max], uso a seguinte classe útil:
A classe funciona para todos os objetos que são
IComparable
. Eu crio uma instância com um certo intervalo:Eu posso validar um argumento
ou fixe o argumento no intervalo:
fonte