Solución:
Sorprendido de que nadie haya publicado la versión de C ++ con seguridad de tipos todavía:
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
Beneficios:
- Realmente implementa signum (-1, 0 o 1). Las implementaciones aquí que usan copysign solo devuelven -1 o 1, que no es signum. Además, algunas implementaciones aquí están devolviendo un float (o T) en lugar de un int, lo que parece un desperdicio.
- Funciona para ints, flotadores, dobles, cortos sin firmar o cualquier tipo personalizado construible a partir del entero 0 y ordenable.
- ¡Rápido!
copysign
es lento, especialmente si necesita promover y luego reducir nuevamente. Esto no tiene ramas y se optimiza de manera excelente. - ¡Cumple con los estándares! El truco de bitshift es genial, pero solo funciona para algunas representaciones de bits y no funciona cuando tienes un tipo sin firmar. Podría proporcionarse 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 un redondeo prematuro a cero.
Advertencias:
- Es una plantilla, por lo que la compilación puede llevar más tiempo en algunas circunstancias.
- Aparentemente, algunas personas piensan en el uso de una función de biblioteca estándar nueva, algo esotérica y muy lenta que ni siquiera implementa signum es más comprensible.
-
los
< 0
parte del cheque activa GCC-Wtype-limits
advertencia cuando se crea una instancia para un tipo sin firmar. Puede 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).
No conozco una función estándar para ello. Sin embargo, aquí hay una forma interesante de escribirlo:
(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)
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 coma flotante están firmados: (+0) dará como resultado +1 y (-0) dará como resultado -1.