Quiero una función que devuelva -1 para números negativos y +1 para números positivos. http://en.wikipedia.org/wiki/Sign_function Es bastante fácil escribir el mío, pero parece algo que debería estar en una biblioteca estándar en alguna parte.
Editar: Específicamente, estaba buscando una función trabajando en flotantes.
x==0
. Según IEEE 754 , el cero negativo y el cero positivo deberían compararse como iguales.Respuestas:
Sorprendido, nadie ha publicado la versión C ++ segura de tipos todavía:
Beneficios:
copysign
es lento, especialmente si necesita promocionar y luego volver a reducir. Esto no tiene ramificaciones y se optimiza excelentementeAdvertencias:
La
< 0
parte de la verificación activa la-Wtype-limits
advertencia de GCC cuando se instancia para un tipo sin signo. Puede evitar esto usando algunas sobrecargas:(Lo cual es un buen ejemplo de la primera advertencia).
fuente
std::copysign
parece dar como resultado un código excelente para mí: 4 instrucciones (en línea), sin ramificación, usando completamente la FPU. La receta dada en esta respuesta, por el contrario, genera un código mucho peor (muchas más instrucciones, incluida una multiplicación, moviéndose hacia adelante y hacia atrás entre la unidad entera y la FPU) ...copysign
a un int, promueve flotar / doble, y debe reducirse nuevamente al regresar. Su compilador puede optimizar esa promoción, pero no puedo encontrar nada que sugiera que está garantizado por el estándar. Además, para implementar signum a través de copysign, debe manejar manualmente el caso 0; asegúrese de incluirlo en cualquier comparación de rendimiento.No sé de una función estándar para ello. Aquí hay una forma interesante de escribirlo:
Aquí hay una forma más legible de hacerlo:
Si le gusta el operador ternario, puede hacer esto:
fuente
x==0
.<
,>
... dará 1 si la relación especificada es verdadera y 0 si es falsa"0
es "falso"; cualquier otro valor es "verdadero"; sin embargo, los operadores relacionales y de igualdad siempre regresan0
o1
(ver Estándar 6.5.8 y 6.5.9). - el valor de la expresióna * (x == 42)
es0
oa
.copysign
para integralx
incluso si lo tuviera disponible.Hay una función de biblioteca matemática C99 llamada copysign (), que toma el signo de un argumento y el valor absoluto del otro:
le dará un resultado de +/- 1.0, dependiendo del signo de valor. Tenga en cuenta que los ceros en coma flotante están firmados: (+0) producirá +1 y (-0) producirá -1.
fuente
Parece que la mayoría de las respuestas omitieron la pregunta original.
No está en la biblioteca estándar, sin embargo, hay una
copysign
que se puede usar casi de la misma maneracopysign(1.0, arg)
y hay una verdadera función de signoboost
, que también podría ser parte de la norma.http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_functions.html
fuente
Aparentemente, la respuesta a la pregunta del póster original es no. No hay una
sgn
función estándar de C ++ .fuente
copysign()
no hará que su primer parámetro sea 0.0 si el segundo es 0.0. En otras palabras, John tiene razón.Sí, dependiendo de la definición.
C99 y posterior tiene la
signbit()
macro en<math.h>
Sin embargo, OP quiere algo un poco diferente.
Más adentro:
El post no es específica en los siguientes casos:
x = 0.0, -0.0, +NaN, -NaN
.Un clásico
signum()
vuelve+1
sobrex>0
,-1
enx<0
y0
enx==0
.Muchas respuestas ya han cubierto eso, pero no abordan
x = -0.0, +NaN, -NaN
. Muchos están orientados a un punto de vista entero que generalmente carece de Not-a-Numbers ( NaN ) y -0.0 .Las respuestas típicas funcionan como
signnum_typical()
On-0.0, +NaN, -NaN
, regresan0.0, 0.0, 0.0
.En cambio, propongo esta funcionalidad: On
-0.0, +NaN, -NaN
, vuelve-0.0, +NaN, -NaN
.fuente
Más rápido que las soluciones anteriores, incluida la mejor valorada:
fuente
Hay una manera de hacerlo sin ramificación, pero no es muy bonita.
http://graphics.stanford.edu/~seander/bithacks.html
Muchas otras cosas interesantes y demasiado inteligentes en esa página, también ...
fuente
sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
osign = (v > 0) - (v < 0);
.v
es un tipo entero no más ancho que intSi todo lo que desea es probar el signo, use signbit (devuelve verdadero si su argumento tiene un signo negativo). No estoy seguro de por qué querría particularmente -1 o +1 devuelto; Copysign es más conveniente para eso, pero parece que devolverá +1 por cero negativo en algunas plataformas con solo soporte parcial para cero negativo, donde presumiblemente el signbit volvería a ser verdadero.
fuente
if (x < 0)
.En general, no existe una función signum estándar en C / C ++, y la falta de una función tan fundamental le dice mucho acerca de estos lenguajes.
Aparte de eso, creo que los dos puntos de vista mayoritarios sobre el enfoque correcto para definir dicha función son correctos, y la "controversia" al respecto no es un argumento una vez que se tienen en cuenta dos advertencias importantes:
Una función signum siempre debe devolver el tipo de su operando, de manera similar a una
abs()
función, porque el signum generalmente se usa para la multiplicación con un valor absoluto después de que este último se haya procesado de alguna manera. Por lo tanto, el principal caso de uso de signum no son las comparaciones, sino la aritmética, y esta última no debería involucrar costosas conversiones de entero a / desde punto flotante.Los tipos de coma flotante no presentan un solo valor cero exacto: +0.0 puede interpretarse como "infinitamente por encima de cero" y -0.0 como "infinitamente por debajo de cero". Esa es la razón por la cual las comparaciones que involucran cero deben verificar internamente contra ambos valores, y una expresión como
x == 0.0
puede ser peligrosa.Con respecto a C, creo que la mejor manera de avanzar con los tipos integrales es usar la
(x > 0) - (x < 0)
expresión, ya que debería traducirse sin ramificaciones y requiere solo tres operaciones básicas. Defina mejor las funciones en línea que imponen un tipo de retorno que coincida con el tipo de argumento y agregue un C11define _Generic
para asignar estas funciones a un nombre común.Con los valores de punto flotante, creo que las funciones en línea basadas en C11
copysignf(1.0f, x)
,copysign(1.0, x)
ycopysignl(1.0l, x)
son el camino a seguir, simplemente porque también es muy probable que estén libres de ramificación y, además, no requieren convertir el resultado del entero en un punto flotante valor. Probablemente debería comentar de manera destacada que sus implementaciones de coma flotante de signum no devolverán cero debido a las peculiaridades de los valores cero de coma flotante, las consideraciones de tiempo de procesamiento y también porque a menudo es muy útil en la aritmética de coma flotante para recibir el -1 / + correcto 1 signo, incluso para valores cero.fuente
Mi copia de C in a Nutshell revela la existencia de una función estándar llamada copysign que podría ser útil. Parece que copysign (1.0, -2.0) devolvería -1.0 y copysign (1.0, 2.0) devolvería +1.0.
Bastante cerca ¿eh?
fuente
No, no existe en c ++, como en matlab. Yo uso una macro en mis programas para esto.
fuente
#define sign(x) (((x) > 0) - ((x) < 0))
que también es bueno.La respuesta aceptada con la sobrecarga a continuación de hecho no activa los límites de tipo .
Para C ++ 11, una alternativa podría ser.
Para mí, no activa ninguna advertencia en GCC 5.3.1.
fuente
-Wunused-parameter
advertencia solo use parámetros sin nombre.Poco fuera de tema, pero yo uso esto:
y encontré que la primera función, la que tiene dos argumentos, es mucho más útil de "estándar" sgn (), porque se usa con mayor frecuencia en código como este:
vs.
no hay conversión para tipos sin signo y sin menos adicional.
de hecho, tengo este código usando sgn ()
fuente
La pregunta es antigua pero ahora existe este tipo de función deseada. Agregué un contenedor con not, shift izquierdo y dec.
Puede usar una función de contenedor basada en signbit de C99 para obtener el comportamiento exacto deseado (vea el código más abajo).
NB: uso el operando no ("!") Porque el valor de retorno de signbit no se especifica como 1 (aunque los ejemplos nos permiten pensar que siempre sería así), pero es cierto para un número negativo:
Luego, multiplico por dos con desplazamiento a la izquierda ("<< 1"), lo que nos dará 2 para un número positivo y 0 para uno negativo y finalmente disminuyo por 1 para obtener 1 y -1 para números positivos y negativos respectivamente según lo solicitado por OP.
fuente
Si bien la solución entera en la respuesta aceptada es bastante elegante, me molestó que no pudiera devolver NAN para tipos dobles, por lo que la modifiqué un poco.
Tenga en cuenta que devolver un NAN de coma flotante en lugar de un código rígido
NAN
hace que el bit de signo se establezca en algunas implementaciones , por lo que el resultadoval = -NAN
yval = NAN
será idénticonan
pase lo-nan
que pase (si prefiere un " " resultado sobre un unabs(val)
antes del regreso ...)fuente
Puede usar el
boost::math::sign()
método desdeboost/math/special_functions/sign.hpp
si está disponible boost.fuente
Aquí hay una implementación amigable de ramificación:
A menos que sus datos tengan ceros como la mitad de los números, aquí el predictor de rama elegirá una de las ramas como la más común. Ambas ramas solo implican operaciones simples.
Alternativamente, en algunos compiladores y arquitecturas de CPU, una versión completamente sin ramificaciones puede ser más rápida:
Esto funciona para el formato de punto flotante binario de doble precisión IEEE 754: binary64 .
fuente
Esta función asume:
fuente
copysign
; si está usandostatic_assert
, tiene C ++ 11, y bien podría usarlocopysign
.fuente
¿Por qué usar operadores ternarios y if-else cuando simplemente puede hacer esto?
fuente
x == INT_MIN
.