c++ - sign function
¿Hay una función de signo estándar(signum, sgn) en C/C++? (23)
¿Hay una función de signo estándar (signum, sgn) en C / C ++?
Sí, dependiendo de la definición.
C99 y posteriores tienen la macro signbit()
en <math.h>
int signbit
(x
flotante real);
La macrosignbit
devuelve un valor distinto de cero si y solo si el signo del valor de su argumento es negativo. C11 §7.12.3.6
Sin embargo, OP quiere algo un poco diferente.
Quiero una función que devuelva -1 para números negativos y +1 para números positivos. ... una función que trabaja en flotadores.
#define signbit_p1_or_n1(x) ((signbit(x) ? -1 : 1)
Más adentro:
La publicación no es específica en los siguientes casos, x = 0.0, -0.0, +NaN, -NaN
.
Un signum()
clásico signum()
devuelve +1
en x>0
, -1
en x>0
y 0
en x==0
.
Muchas respuestas ya han cubierto eso, pero no abordan x = -0.0, +NaN, -NaN
. Muchos están orientados para un punto de vista entero que generalmente carece de números de -0.0 ( NaN ) y -0.0 .
Las respuestas típicas funcionan como signnum_typical()
En -0.0, +NaN, -NaN
, devuelven 0.0, 0.0, 0.0
.
int signnum_typical(double x) {
if (x > 0.0) return 1;
if (x < 0.0) return -1;
return 0;
}
En su lugar, proponga esta funcionalidad: En -0.0, +NaN, -NaN
, devuelve -0.0, +NaN, -NaN
.
double signnum_c(double x) {
if (x > 0.0) return 1.0;
if (x < 0.0) return -1.0;
return x;
}
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 la mía, pero parece algo que debería estar en una biblioteca estándar en algún lugar.
Edit: Específicamente, estaba buscando una función trabajando en flotadores.
¿Por qué usar operadores ternarios y if-else cuando puedes simplemente hacer esto?
#define sgn(x) x==0 ? 0 : x/abs(x)
Aparentemente, la respuesta a la pregunta del cartel original es no. No hay una función estándar de C ++ sgn
.
En general, no hay una función signum estándar en C / C ++, y la falta de una función tan fundamental le dice mucho sobre estos idiomas.
Aparte de eso, creo que ambos puntos de vista mayoritarios sobre el enfoque correcto para definir dicha función son, en cierto modo, correctos, y la "controversia" sobre el tema 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 función
abs()
, porque signum generalmente se usa para la multiplicación con un valor absoluto después de que esta última 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 debe implicar ninguna conversión costosa de entero a / desde punto flotante.Los tipos de punto flotante no cuentan con un solo valor de cero exacto: +0.0 puede interpretarse como "infinitesimalmente por encima de cero", y -0.0 como "infinitesimalmente por debajo de cero". Esa es la razón por la que las comparaciones con cero deben compararse internamente con ambos valores, y una expresión como
x == 0.0
puede ser peligrosa.
Con respecto a C, creo que la mejor forma de avanzar con los tipos integrales es, de hecho, usar la expresión (x > 0) - (x < 0)
, ya que debería traducirse de forma libre de sucursales y solo requiere 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 C11 define _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 copysignf(1.0f, x)
C11 copysignf(1.0f, x)
, copysign(1.0, x)
y copysignl(1.0l, x)
son el camino a seguir, simplemente porque también son muy probables no debe tener sucursales y, además, no es necesario volver a convertir el resultado del entero en un valor de punto flotante. Probablemente debería comentar de manera prominente que sus implementaciones de signum de punto flotante no devolverán cero debido a las peculiaridades de los valores de cero de punto flotante, las consideraciones de tiempo de procesamiento, y también porque a menudo es muy útil en aritmética de punto flotante para recibir el -1 / + correcto 1 signo, incluso para valores cero.
Hay una función de biblioteca matemática C99 llamada copysign (), que toma el signo de un argumento y el valor absoluto del otro:
result = copysign(1.0, value) // double
result = copysignf(1.0, value) // float
result = copysignl(1.0, value) // long double
le dará un resultado de +/- 1.0, dependiendo del signo de valor. Tenga en cuenta que los ceros de punto flotante están firmados: (+0) producirá +1, y (-0) producirá -1.
Hay una manera de hacerlo sin ramificación, pero no es muy bonita.
sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
http://graphics.stanford.edu/~seander/bithacks.html
Muchas otras cosas interesantes, demasiado inteligentes en esa página, también ...
La respuesta aceptada con la sobrecarga a continuación no dispara -Wtype-limits, sin embargo, dispara -Wunused-parámetro en el argumento is_signed
.
template <typename T> inline constexpr
int signum(T x, std::false_type is_signed) {
return T(0) < x;
}
template <typename T> inline constexpr
int signum(T x, std::true_type is_signed) {
return (T(0) < x) - (x < T(0));
}
template <typename T> inline constexpr
int signum(T x) {
return signum(x, std::is_signed<T>());
}
Para C ++ 11 una alternativa podría ser.
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, int>::type
inline constexpr signum(T x) {
return T(0) < x;
}
template <typename T>
typename std::enable_if<std::is_signed<T>::value, int>::type
inline constexpr signum(T x) {
return (T(0) < x) - (x < T(0));
}
Para mí no activa ninguna advertencia en GCC 5.3.1
Más rápido que las soluciones anteriores, incluyendo la más alta calificación:
(x < 0) ? -1 : (x > 0)
Me encontré con esto justo hoy. Muy bien, no hay una forma estándar , pero ...
Ya que el OP solo necesitaba ampliar el rango de salida y volver a centrarlo en 0, (-1 a 1 no 0 a 1) ¿por qué no simplemente duplicarlo y restar 1?
Utilicé esto:
(x <0) * 2-1
O, forzando un poco de cambio:
(x <0) << 1-1
Pero el compilador probablemente optimizará eso de todos modos.
Mi copia de C en pocas palabras 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?
No sé de una función estándar para ello. Aquí hay una manera interesante de escribirlo sin embargo:
(x > 0) - (x < 0)
Aquí hay una forma más legible de hacerlo:
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
Si te gusta el operador ternario puedes hacer esto:
(x > 0) ? 1 : ((x < 0) ? -1 : 0)
No, no existe en c ++, como en matlab. Yo uso una macro en mis programas para esto.
#define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) )
Parece que la mayoría de las respuestas pasaron por alto la pregunta original.
¿Hay una función de signo estándar (signum, sgn) en C / C ++?
No en la biblioteca estándar, pero hay un boost
, que también podría ser parte de la norma.
#include <boost/math/special_functions/sign.hpp>
//Returns 1 if x > 0, -1 if x < 0, and 0 if x is zero.
template <class T>
inline int sign (const T& z);
Poco fuera de tema, pero uso esto:
template<typename T>
constexpr int sgn(const T &a, const T &b) noexcept{
return (a > b) - (a < b);
}
template<typename T>
constexpr int sgn(const T &a) noexcept{
return sgn(a, T(0));
}
y encontré que la primera función, la que tiene dos argumentos, es mucho más útil que sgn () "estándar", porque se usa con más frecuencia en códigos como este:
int comp(unsigned a, unsigned b){
return sgn( int(a) - int(b) );
}
contra
int comp(unsigned a, unsigned b){
return sgn(a, b);
}
no hay conversión para los tipos sin firmar y no hay ningún signo de menos adicional.
de hecho tengo este pedazo de código usando sgn ()
template <class T>
int comp(const T &a, const T &b){
log__("all");
if (a < b)
return -1;
if (a > b)
return +1;
return 0;
}
inline int comp(int const a, int const b){
log__("int");
return a - b;
}
inline int comp(long int const a, long int const b){
log__("long");
return sgn(a, b);
}
Puede usar el método boost::math::sign()
de boost/math/special_functions/sign.hpp
si está disponible boost.
Qué pasa:
int sgn = x/fabs(x);
debería funcionar bastante bien
Si bien la solución de enteros en la respuesta aceptada es bastante elegante, me molestó que no pudiera devolver NAN para tipos dobles, así que la modifiqué ligeramente.
template <typename T> double sgn(T val) {
return double((T(0) < val) - (val < T(0)))/(val == val);
}
Tenga en cuenta que devolver una NAN de punto flotante en lugar de una NAN
codificada dura hace que el bit de signo se establezca en algunas implementaciones , por lo que la salida para val = -NAN
y val = NAN
va a ser idéntica sin importar qué (si prefiere una La salida " nan
" sobre un -nan
puede poner un abs(val)
antes de la devolución ...)
Si todo lo que desea es probar el signo, use signbit (devuelve true si su argumento tiene un signo negativo). No estoy seguro de por qué querrías particularmente -1 o +1 devuelto; Copysign es más conveniente para eso, pero parece que devolverá +1 para el cero negativo en algunas plataformas con solo soporte parcial para el negativo, donde probablemente signbit probablemente se devolvería verdadero.
Sorprendido, nadie ha publicado todavía la versión de C ++ sin ramas y segura para el tipo:
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
Beneficios:
- En realidad implementa signum (-1, 0 o 1). Las implementaciones aquí utilizando copysign solo devuelven -1 o 1, que no es signum. Además, algunas implementaciones aquí están devolviendo un flotante (o T) en lugar de un int, lo que parece un desperdicio.
- Funciona para ints, flotadores, dobles, cortocircuitos sin firmar o cualquier tipo personalizado construible a partir de entero 0 y ordenable.
- ¡Rápido!
copysign
es lento, especialmente si necesita promover y luego reducir de nuevo. Esto es sin sucursales y optimiza excelentemente. - Cumplimiento de las normas! El trocito de bitshift está limpio, pero solo funciona para algunas representaciones de bits, y no funciona cuando tienes un tipo sin firmar. Podría ser provisto como una especialización manual cuando sea apropiado.
- ¡Preciso! Las comparaciones simples con cero pueden mantener la representación interna de alta precisión de la máquina (por ejemplo, 80 bits en x87) y evitar una ronda prematura a cero.
Advertencias:
- Es una plantilla por lo que tardará una eternidad en compilar.
- Aparentemente, algunas personas piensan que el uso de una nueva función de biblioteca estándar, algo esotérica y muy lenta, que ni siquiera implementa signum es más comprensible.
La parte
< 0
de la verificación desencadena la advertencia-Wtype-limits
GCC cuando se-Wtype-limits
instancia para un tipo sin firma. Puedes evitar esto usando algunas sobrecargas:template <typename T> inline constexpr int signum(T x, std::false_type is_signed) { return T(0) < x; } template <typename T> inline constexpr int signum(T x, std::true_type is_signed) { return (T(0) < x) - (x < T(0)); } template <typename T> inline constexpr int signum(T x) { return signum(x, std::is_signed<T>()); }
(Que es un buen ejemplo de la primera advertencia.)
utilizar:
`#define sgn(x) (x<0)`
por ejemplo:
`if(sng(n)) { etc ....}`
O puede que quieras usar un código elaborado, pero primero el casting:
inline bool sgn_long(long x) { return ((x<0)? true: false); }
#include<iostream>
#include<math.h>
using namespace std;
int main()
{
float k=10;
cout<<bool signbit(k); /* bool signbit(arg) will return "0" if arg passed is +
else "1" */
return 0;
}
Es posible que el código anterior no sirva para su propósito (obtener 1 o -1), pero ciertamente hace que sea más fácil distinguir el signo del tipo de datos (int, float, double, etc.)
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
int sign(float n)
{
union { float f; std::uint32_t i; } u { n };
return 1 - ((u.i >> 31) << 1);
}
Esta función asume:
- representación binaria32 de números de punto flotante
- un compilador que hace una excepción sobre la regla estricta de alias cuando se usa una unión nombrada