Короче принятого ответа, та же идея:
(SELECT TRIM('2016-01-05' + INTERVAL a + b DAY) date
FROM
(SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3
UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
UNION SELECT 8 UNION SELECT 9 ) d,
(SELECT 0 b UNION SELECT 10 UNION SELECT 20
UNION SELECT 30 UNION SELECT 40) m
WHERE '2016-01-05' + INTERVAL a + b DAY <= '2016-01-21')
Удивлен, что еще никто не опубликовал версию C ++ без веток и типов:
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
Преимущества:
copysign
медленно, особенно если вам нужно продвинуться, а затем снова сузиться. Это не имеет ответвлений и отлично оптимизирует Предостережения:
Часть < 0
проверки вызывает предупреждение -Wtype-limits
GCC при создании экземпляра для типа без знака. Вы можете избежать этого, используя некоторые перегрузки:
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>());
}
(что является хорошим примером первого предупреждения.)
используйте:
`#define sgn(x) (x<0)`
, например:
`if(sng(n)) { etc ....}`
Или, возможно, вы захотите использовать какой-то сложный код, но сначала выполните приведение:
inline bool sgn_long(long x)
{
return ((x<0)? true: false);
}
Как насчет:
int sgn = x/fabs(x);
это должно получиться довольно хорошо.
Я столкнулся с этим только сегодня. Так хорошо, стандартного способа нет, но ...
Поскольку ОП просто необходимо увеличить выходной диапазон и перецентрировать его на 0, (от -1 до 1, а не от 0 до 1), почему бы просто не удвоить его и вычесть 1?
Я использовал это:
(x < 0) * 2-1
Или, принудительно сдвигая бит:
(x < 0) < < 1 -1
Но компилятор, вероятно, все равно оптимизирует это.
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
Вот реализация, подходящая для ветвлений:
inline int signum(const double x) {
if(x == 0) return 0;
return (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}
Если ваши данные не имеют нулей в качестве половины чисел, здесь предиктор ветвлений выберет одну из ветвей в качестве наиболее распространенной. Обе ветви включают только простые операции.
В качестве альтернативы, на некоторых компиляторах и архитектурах ЦП версия без ответвлений может быть быстрее:
inline int signum(const double x) {
return (x != 0) *
(1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}
Это работает для двоичного формата двойной точности с плавающей точкой IEEE 754: binary64 .
Вы можете использовать метод boost::math::sign()
из boost/math/special_functions/sign.hpp
, если доступно усиление.
Моя копия C в двух словах раскрывает существование стандартной функции copysign, которая может быть полезна. Похоже, что copysign (1.0, -2.0) вернет -1.0, а copysign (1.0, 2.0) вернет +1.0.
Довольно близко, да?
int sign(float n)
{
union { float f; std::uint32_t i; } u { n };
return 1 - ((u.i >> 31) << 1);
}
Эта функция предполагает:
Немного не по теме, но я использую это:
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));
}
и я обнаружил, что первая функция - с двумя аргументами - гораздо более полезна из "стандартного" sgn (), потому что она чаще всего используется в коде, подобном этому:
int comp(unsigned a, unsigned b){
return sgn( int(a) - int(b) );
}
против
int comp(unsigned a, unsigned b){
return sgn(a, b);
}
нет броска для неподписанных типов и дополнительного минуса.
На самом деле у меня есть этот кусок кода с использованием 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);
}
В общем, в C / C ++ нет стандартной функции signum, и отсутствие такой фундаментальной функции многое говорит вам об этих языках.
Кроме того, я полагаю, что обе точки зрения большинства о правильном подходе к определению такой функции в некотором смысле верны, и «спор» об этом фактически не является аргументом, если принять во внимание два важных предостережения:
Функция signum всегда должна возвращать тип своего операнда, аналогично функции abs()
, потому что signum обычно используется для умножения с абсолютным значением после того, как последний был как-то обработан. Поэтому основной вариант использования signum - это не сравнения, а арифметика, и последний не должен включать дорогостоящие преобразования целых чисел в / из плавающей запятой.
Типы с плавающей запятой не имеют единого точного нулевого значения: +0.0 можно интерпретировать как «бесконечно меньше нуля», а -0.0 - «бесконечно меньше нуля». По этой причине сравнения, включающие ноль, должны внутренне сверяться с обоими значениями, а выражение типа x == 0.0
может быть опасным.
Что касается C, я думаю, что лучший способ продвижения вперед с целочисленными типами - это действительно использовать выражение (x > 0) - (x < 0)
, так как оно должно переводиться без ветвления и требует только трех основных операций. Лучше всего определить встроенные функции, которые обеспечивают возвращаемый тип, соответствующий типу аргумента, и добавить C11 define _Generic
для сопоставления этих функций общему имени.
С значениями с плавающей запятой, я думаю, что встроенные функции, основанные на C11 copysignf(1.0f, x)
, copysign(1.0, x)
и copysignl(1.0l, x)
, - это путь, просто потому, что они также с большой вероятностью свободны от ветвей и дополнительно делают не требует приведения результата из целого числа обратно в значение с плавающей запятой. Вы, вероятно, должны заметить, что ваши реализации с плавающей запятой signum не будут возвращать ноль из-за особенностей значений с плавающей запятой, соображений времени обработки, а также потому, что это часто очень полезно в арифметике с плавающей запятой для получения правильный знак -1 / + 1, даже для нулевых значений.
Зачем использовать троичные операторы и если-иначе, когда вы можете просто сделать это
#define sgn(x) x==0 ? 0 : x/abs(x)
Принятый ответ с приведенной ниже перегрузкой действительно не запускает -Wtype-limit .
template <typename T> inline constexpr
int signum(T x, std::false_type) {
return T(0) < x;
}
template <typename T> inline constexpr
int signum(T x, std::true_type) {
return (T(0) < x) - (x < T(0));
}
template <typename T> inline constexpr
int signum(T x) {
return signum(x, std::is_signed<T>());
}
Для C ++ 11 альтернативой может быть.
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));
}
Для меня это не вызывает каких-либо предупреждений в GCC 5.3.1.
Хотя целочисленное решение в принятом ответе довольно элегантно, меня беспокоило, что оно не сможет вернуть NAN для двойных типов, поэтому я немного изменил его.
template <typename T> double sgn(T val) {
return double((T(0) < val) - (val < T(0)))/(val == val);
}
Обратите внимание, что возврат NAN с плавающей запятой в отличие от жестко запрограммированного NAN
приводит к тому, что бит знака устанавливается в в некоторых реализациях , поэтому вывод для val = -NAN
и val = NAN
будут идентичны независимо от того, что (если вы предпочитаете вывод «nan
» вместо «-nan
, вы можете поставить abs(val)
перед возвращением ...)»
Есть ли стандартная функция знака (signum, sgn) в C / C ++?
Да, в зависимости от определения.
C99 и более поздние имеют макрос signbit()
в <math.h>
int signbit
(в реальном времениx
);
Макросsignbit
возвращает ненулевое значение тогда и только тогда, когда знак значения аргумента отрицательный. C11 §7.12.3.6
И все же OP хочет что-то немного другое.
Я хочу функцию, которая возвращает -1 для отрицательных чисел и +1 для положительных чисел. ... функция, работающая на поплавках.
#define signbit_p1_or_n1(x) ((signbit(x) ? -1 : 1)
Глубже:
Должность не является конкретной в следующих случаях, x = 0.0, -0.0, +NaN, -NaN
.
Классический signum()
возвращает +1
на x>0
, -1
на x>0
и 0
на x==0
.
Многие ответы уже охватили это, но не касаются x = -0.0, +NaN, -NaN
. Многие из них ориентированы на целочисленную точку зрения, в которой обычно отсутствуют не-числа ( NaN ) и -0,0 .
Типичные ответы работают как signnum_typical()
В -0.0, +NaN, -NaN
они возвращают 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;
}
Вместо этого предложите эту функциональность: На -0.0, +NaN, -NaN
он возвращает -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;
}
Быстрее, чем приведенные выше решения, включая решение с самым высоким рейтингом:
(x < 0) ? -1 : (x > 0)
По-видимому, ответ на вопрос автора оригинала - нет. Не существует стандартной C ++ sgn
функции.
Существует функция математической библиотеки C99, называемая copysign (), которая принимает знак одного аргумента и абсолютное значение другого:
result = copysign(1.0, value) // double
result = copysignf(1.0, value) // float
result = copysignl(1.0, value) // long double
даст вам результат +/- 1.0, в зависимости от знака стоимости. Обратите внимание, что нули с плавающей запятой подписаны: (+0) даст +1, а (-0) даст -1.
Если вам нужно только проверить знак, используйте signbit (возвращает true, если его аргумент имеет отрицательный знак). Не уверен, почему вы хотели бы вернуть -1 или +1; copysign более удобен для этого, но, похоже, он вернет +1 для отрицательного нуля на некоторых платформах с лишь частичной поддержкой отрицательного нуля, где signbit предположительно вернул бы true.
Есть способ сделать это без ветвления, но это не очень красиво.
sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
http://graphics.stanford.edu/~seander/bithacks.html
Множество других интересных, слишком умных вещей на этой странице, тоже .. .
Нет, его нет в c ++, как в matlab. Для этого я использую макрос в своих программах.
#define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) )
Я не знаю стандартной функции для этого. Вот интересный способ написать это:
(x > 0) - (x < 0)
Вот более читаемый способ сделать это:
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
Если вам нравится троичный оператор, вы можете сделать это:
(x > 0) ? 1 : ((x < 0) ? -1 : 0)
Кажется, что большинство ответов пропустили первоначальный вопрос.
Есть ли стандартная функция знака (signum, sgn) в C / C ++?
Нет в стандартной библиотеке, однако есть copysign
, который может использоваться почти таким же образом через copysign(1.0, arg)
, и в boost
есть функция истинного знака, которая также может быть частью стандарта.
#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);