Eu quero uma função que retorne -1 para números negativos e +1 para números positivos. http://en.wikipedia.org/wiki/Sign_function É bastante fácil escrever o meu, mas parece que algo deveria estar em uma biblioteca padrão em algum lugar.
Edit: Especificamente, eu estava procurando uma função trabalhando em carros alegóricos.
x==0
. De acordo com a IEEE 754 , o zero negativo e o zero positivo devem ser comparados como iguais.Respostas:
Surpreendido, ninguém postou a versão C ++ com segurança de tipo ainda:
Benefícios:
copysign
é lento, especialmente se você precisar promover e estreitar novamente. Isso é sem ramificação e otimiza excelentementeRessalvas:
A
< 0
parte da verificação aciona o-Wtype-limits
aviso do GCC quando instanciada para um tipo não assinado. Você pode evitar isso usando algumas sobrecargas:(Que é um bom exemplo da primeira advertência.)
fonte
std::copysign
parece resultar em um excelente código para mim: 4 instruções (incluídas), sem ramificação, usando inteiramente o FPU. A receita dada nesta resposta, pelo contrário, gera código muito pior (muitos mais instruções, incluindo uma multiplicação, e para trás entre a unidade inteira e FPU em movimento) ...copysign
um int, ele promove flutuar / dobrar e deve diminuir novamente no retorno. Seu compilador pode otimizar essa promoção, mas não consigo encontrar nada sugerindo que seja garantido pelo padrão. Também para implementar o signum via copysign, você precisa lidar manualmente com o caso 0 - inclua isso em qualquer comparação de desempenho.Não conheço uma função padrão para isso. Aqui está uma maneira interessante de escrevê-lo:
Aqui está uma maneira mais legível de fazer isso:
Se você gosta do operador ternário, pode fazer o seguinte:
fonte
x==0
.<
,>
... produzirá 1 se a relação especificada for verdadeira e 0 se for falsa"0
é "false"; qualquer outro valor é "verdadeiro"; no entanto, os operadores relacionais e de igualdade sempre retornam0
ou1
(consulte Padrão 6.5.8 e 6.5.9). - o valor da expressãoa * (x == 42)
é0
oua
.copysign
integralx
mesmo se o tivesse disponível.Existe uma função da biblioteca matemática C99 chamada copysign (), que recebe o sinal de um argumento e o valor absoluto do outro:
resultará em +/- 1,0, dependendo do sinal do valor. Observe que os zeros de ponto flutuante são assinados: (+0) produzirá +1 e (-0) produzirá -1.
fonte
Parece que a maioria das respostas perdeu a pergunta original.
Não na biblioteca padrão, no entanto, existe o
copysign
que pode ser usado quase da mesma maneira viacopysign(1.0, arg)
e existe uma verdadeira função de sinalboost
, que também pode fazer parte do padrão.http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_functions.html
fonte
Aparentemente, a resposta para a pergunta do pôster original é não. Não há função C ++ padrão
sgn
.fonte
copysign()
não fará seu primeiro parâmetro 0,0 se o segundo for 0,0. Em outras palavras, John está correto.Sim, dependendo da definição.
C99 e posterior possui a
signbit()
macro em<math.h>
No entanto, o OP quer algo um pouco diferente.
Mais profundo:
O post não é específico nos seguintes casos:
x = 0.0, -0.0, +NaN, -NaN
.Um clássico
signum()
retorna+1
nox>0
,-1
nox<0
e0
nox==0
.Muitas respostas já abordaram isso, mas não abordam
x = -0.0, +NaN, -NaN
. Muitos são voltados para um ponto de vista inteiro que geralmente não possui números não-números ( NaN ) e -0,0 .Respostas típicas funcionam como
signnum_typical()
Ativado-0.0, +NaN, -NaN
, elas retornam0.0, 0.0, 0.0
.Em vez disso, proponho esta funcionalidade: Sim
-0.0, +NaN, -NaN
, ela retorna-0.0, +NaN, -NaN
.fonte
Mais rápidas que as soluções acima, incluindo a mais alta classificada:
fonte
Existe uma maneira de fazer isso sem ramificação, mas não é muito bonito.
http://graphics.stanford.edu/~seander/bithacks.html
Muitas outras coisas interessantes e excessivamente inteligentes nessa página também ...
fonte
sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
ousign = (v > 0) - (v < 0);
.v
é um tipo inteiro não mais largo do que intSe tudo que você deseja é testar o sinal, use signbit (retorna true se o argumento tiver um sinal negativo). Não sabe por que você deseja que -1 ou +1 sejam retornados; copysign é mais conveniente para isso, mas parece que ele retornará +1 para zero negativo em algumas plataformas com apenas suporte parcial para zero negativo, em que presumivelmente o signbit retornaria true.
fonte
if (x < 0)
.Em geral, não há função de sinalização padrão no C / C ++, e a falta de uma função fundamental diz muito sobre essas linguagens.
Além disso, acredito que os dois pontos de vista da maioria sobre a abordagem correta para definir uma função estão de certa maneira corretos, e a "controvérsia" sobre ela é na verdade um argumento, uma vez que você leva em consideração duas advertências importantes:
Uma função signum sempre deve retornar o tipo de seu operando, da mesma forma que uma
abs()
função, porque signum é geralmente usado para multiplicação com um valor absoluto depois que o último foi processado de alguma forma. Portanto, o principal caso de uso de signum não é comparações, mas aritmética, e o último não deve envolver conversões caras de número inteiro para ponto flutuante.Os tipos de ponto flutuante não apresentam um único valor exato de zero: +0,0 pode ser interpretado como "infinitesimalmente acima de zero" e -0,0 como "infinitesimalmente abaixo de zero". Essa é a razão pela qual comparações envolvendo zero devem verificar internamente os dois valores, e uma expressão como
x == 0.0
pode ser perigosa.Em relação a C, acho que o melhor caminho a seguir com tipos integrais é realmente usar a
(x > 0) - (x < 0)
expressão, pois ela deve ser traduzida de forma livre de ramificação e requer apenas três operações básicas. É melhor definir funções embutidas que impõem um tipo de retorno correspondente ao tipo de argumento e adicionar um C11define _Generic
para mapear essas funções para um nome comum.Com valores de ponto flutuante, acho funções inline baseado em C11
copysignf(1.0f, x)
,copysign(1.0, x)
ecopysignl(1.0l, x)
são o caminho a percorrer, simplesmente porque eles são também altamente provável que seja livre de ramo, e, adicionalmente, não necessitam de conversão do resultado de inteiro de volta para um ponto flutuante valor. Provavelmente, você deve comentar com destaque que suas implementações de signum de ponto flutuante não retornarão zero por causa das peculiaridades dos valores de ponto flutuante zero, considerações sobre o tempo de processamento e também porque geralmente é muito útil na aritmética de ponto flutuante receber o -1 / + correto 1 sinal, mesmo para valores zero.fonte
Minha cópia de C em poucas palavras revela a existência de uma função padrão chamada copysign que pode ser útil. Parece que o copysign (1.0, -2.0) retornaria -1.0 e o copysign (1.0, 2.0) retornaria +1.0.
Bem perto né?
fonte
Não, ele não existe no c ++, como no matlab. Eu uso uma macro em meus programas para isso.
fonte
#define sign(x) (((x) > 0) - ((x) < 0))
que também é bom.A resposta aceita com a sobrecarga abaixo de fato não aciona -Wtype-limits .
Para C ++ 11, uma alternativa poderia ser.
Para mim, ele não dispara nenhum aviso no GCC 5.3.1.
fonte
-Wunused-parameter
aviso, use parâmetros sem nome.Pouco fora do tópico, mas eu uso isso:
e eu achei a primeira função - aquela com dois argumentos, para ser muito mais útil a partir de "standard" sgn (), porque é mais frequentemente usada em códigos como este:
vs.
não há conversão para tipos não assinados e nenhum menos adicional.
na verdade, eu tenho esse pedaço de código usando sgn ()
fonte
A questão é antiga, mas agora existe esse tipo de função desejada. Eu adicionei um invólucro com não, shift esquerdo e dec.
Você pode usar uma função de invólucro baseada no signbit do C99 para obter o comportamento exato desejado (consulte o código mais abaixo).
NB: Eu uso o operando não ("!") Porque o valor de retorno do signbit não é especificado como 1 (mesmo que os exemplos pensemos que seria sempre assim), mas verdadeiro para um número negativo:
Em seguida, multiplico por dois com o deslocamento à esquerda ("<< 1"), que nos dará 2 para um número positivo e 0 para um número negativo e, finalmente, decrementaremos 1 para obter 1 e -1 para os números respectivamente positivos e negativos, conforme solicitado por OP.
fonte
Embora a solução inteira na resposta aceita seja bastante elegante, me incomodou o fato de não poder retornar NAN para tipos duplos, por isso modifiquei-a levemente.
Observe que o retorno de uma NAN de ponto flutuante em oposição a um código codificado
NAN
faz com que o bit de sinal seja definido em algumas implementações ; portanto, a saídaval = -NAN
e a saídaval = NAN
serão idênticas, independentemente do que for (se você preferir umanan
saída " " a uma,-nan
você pode colocar umabs(val)
antes do retorno ...)fonte
Você pode usar o
boost::math::sign()
método a partir deboost/math/special_functions/sign.hpp
se o impulso estiver disponível.fonte
Aqui está uma implementação amigável para ramificação:
A menos que seus dados tenham zeros como metade dos números, aqui o preditor de ramificação escolherá uma das ramificações como a mais comum. Ambas as ramificações envolvem apenas operações simples.
Como alternativa, em alguns compiladores e arquiteturas de CPU, uma versão completamente sem ramificação pode ser mais rápida:
Isso funciona para o formato de ponto flutuante binário de precisão dupla IEEE 754: binary64 .
fonte
Esta função assume:
fonte
copysign
; se você estiver usando,static_assert
possui C ++ 11 e pode muito bem usá-locopysign
.fonte
Por que usar operadores ternários e if-else quando você pode simplesmente fazer isso
fonte
x == INT_MIN
.