Я хочу функцию, которая возвращает -1 для отрицательных чисел и +1 для положительных чисел. http://en.wikipedia.org/wiki/Sign_function Достаточно легко написать мою собственную, но это похоже на то, что должно быть где-то в стандартной библиотеке.
Редактировать: В частности, я искал функцию, работающую на поплавках.
x==0
. Согласно IEEE 754 отрицательный ноль и положительный ноль должны сравниваться как равные.Ответы:
Удивило, что никто еще не опубликовал типобезопасную версию C ++:
Льготы:
copysign
медленный, особенно если вам нужно продвинуться, а затем снова сузить. Это не имеет ответвлений и отлично оптимизируетПредостережения:
< 0
Часть проверки триггеров ССЗ-Wtype-limits
предупреждение , когда экземпляр для неподписанных типа. Вы можете избежать этого, используя некоторые перегрузки:(Что является хорошим примером первого предупреждения.)
источник
std::copysign
кажется, дает мне отличный код: 4 инструкции (встроенные), без ветвления, полностью с использованием FPU. Рецепт, приведенный в этом ответе, напротив, генерирует гораздо худший код (гораздо больше инструкций, включая умножение, перемещение назад и вперед между целочисленными единицами и FPU) ...copysign
int, он продвигает float / double и должен снова сужаться при возврате. Ваш компилятор может оптимизировать эту рекламу, но я не могу найти ничего, что могло бы гарантировать стандарт. Также для реализации signum через copysign вам нужно вручную обработать регистр 0 - пожалуйста, убедитесь, что вы включили это в любое сравнение производительности.Я не знаю стандартной функции для этого. Вот интересный способ написать это:
Вот более читаемый способ сделать это:
Если вам нравится троичный оператор, вы можете сделать это:
источник
x==0
.<
,>
... сдаст 1 , если указанное соотношение справедливо и 0 , если оно ложно»0
«ложь»; любое другое значение "true"; однако операторы отношения и равенства всегда возвращают0
или1
(см. Стандарты 6.5.8 и 6.5.9). - значение выраженияa * (x == 42)
является либо0
илиa
.copysign
для интеграла,x
даже если бы он был у меня в наличии.Существует функция математической библиотеки C99, называемая copysign (), которая принимает знак одного аргумента и абсолютное значение другого:
даст вам результат +/- 1,0, в зависимости от знака значения. Обратите внимание, что нули с плавающей точкой подписаны: (+0) даст +1, а (-0) даст -1.
источник
Кажется, что большинство ответов пропустили оригинальный вопрос.
Не в стандартной библиотеке, однако, есть то,
copysign
что можно использовать почти так же, как через,copysign(1.0, arg)
и есть функция истинного знакаboost
, которая также может быть частью стандарта.http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_functions.html
источник
Судя по всему, ответ на вопрос автора оригинала - нет. Там нет стандартной
sgn
функции C ++ .источник
copysign()
не сделает ваш первый параметр 0.0, если второй 0.0. Другими словами, Джон прав.Да, в зависимости от определения.
C99 и позже имеет
signbit()
макрос в<math.h>
И все же ОП хочет что-то немного другое.
Глубже:
Сообщение не является специфичным в следующих случаях:
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()
On-0.0, +NaN, -NaN
, они возвращаются0.0, 0.0, 0.0
.Вместо этого я предлагаю эту функциональность:
-0.0, +NaN, -NaN
он возвращается-0.0, +NaN, -NaN
.источник
Быстрее, чем вышеперечисленные решения, в том числе с самым высоким рейтингом:
источник
Есть способ сделать это без ветвления, но это не очень красиво.
http://graphics.stanford.edu/~seander/bithacks.html
Много других интересных, слишком умных вещей на этой странице тоже ...
источник
sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
илиsign = (v > 0) - (v < 0);
.v
это целочисленный тип, не шире, чем intЕсли все, что вам нужно, это проверить знак, используйте signbit (возвращает true, если его аргумент имеет отрицательный знак). Не уверен, почему вы хотели бы вернуть -1 или +1; copysign более удобен для этого, но, похоже, он вернет +1 для отрицательного нуля на некоторых платформах с частичной поддержкой отрицательного нуля, где signbit предположительно вернул бы true.
источник
if (x < 0)
.В общем, в C / C ++ нет стандартной функции signum, и отсутствие такой фундаментальной функции многое говорит вам об этих языках.
Кроме того, я полагаю, что обе точки зрения большинства о правильном подходе к определению такой функции в некотором смысле правильны, и «спор» об этом фактически не является аргументом, если принять во внимание два важных предостережения:
Функция signum всегда должна возвращать тип своего операнда, аналогично
abs()
функции, потому что signum обычно используется для умножения с абсолютным значением после того, как последний был каким-либо образом обработан. Следовательно, основным вариантом использования signum являются не сравнения, а арифметика, и последний не должен включать какие-либо дорогостоящие преобразования целых чисел в / из плавающей запятой.Типы с плавающей запятой не имеют единственного точного нулевого значения: +0.0 можно интерпретировать как «бесконечно меньше нуля», а -0.0 - «бесконечно меньше нуля». По этой причине сравнения, включающие ноль, должны внутренне сверяться с обоими значениями, и такое выражение
x == 0.0
может быть опасным.Что касается C, я думаю, что лучший способ продвижения вперед с интегральными типами - это действительно использовать
(x > 0) - (x < 0)
выражение, так как оно должно переводиться без ветвления и требует только трех основных операций. Лучше всего определить встроенные функции, которые обеспечивают возвращаемый тип, соответствующий типу аргумента, и добавить C11define _Generic
для сопоставления этих функций общему имени.С плавающей точкой, я думаю , что встроенные функции , основанные на С11
copysignf(1.0f, x)
,copysign(1.0, x)
иcopysignl(1.0l, x)
это путь, просто потому , что они также весьма вероятно, будет отделение свободной, и , кроме того , не требуют заливки результат целочисленного обратно в плавающей точкой ценность. Вы, вероятно, должны заметить, что ваши реализации signum с плавающей запятой не будут возвращать ноль из-за особенностей нулевых значений с плавающей запятой, соображений времени обработки, а также потому, что это часто очень полезно в арифметике с плавающей запятой для получения правильного -1 / + 1 знак, даже для нулевых значений.источник
Моя копия C в двух словах показывает существование стандартной функции copysign, которая может быть полезна. Похоже, что copysign (1.0, -2.0) вернет -1.0, а copysign (1.0, 2.0) вернет +1.0.
Довольно близко, а?
источник
Нет, его нет в c ++, как в matlab. Я использую макрос в моих программах для этого.
источник
#define sign(x) (((x) > 0) - ((x) < 0))
что это тоже хорошо.Принятый ответ с приведенной ниже перегрузкой действительно не вызывает -Wtype-limit .
Для C ++ 11 альтернативой может быть.
Для меня это не вызывает никаких предупреждений на GCC 5.3.1.
источник
-Wunused-parameter
предупреждения, просто используйте неназванные параметры.Немного не по теме, но я использую это:
и я обнаружил, что первая функция - с двумя аргументами - гораздо более полезна из «стандартного» sgn (), потому что она чаще всего используется в коде, подобном следующему:
против
здесь нет броска для неподписанных типов и дополнительного минуса.
на самом деле у меня есть этот кусок кода с помощью sgn ()
источник
Вопрос старый, но теперь есть такая желаемая функция. Я добавил обертку с not, left shift и dec.
Вы можете использовать функцию-оболочку на основе signbit из C99 , чтобы получить точное желаемое поведение (см. Код ниже).
NB: я использую операнд не ("!"), Потому что возвращаемое значение signbit не указано равным 1 (хотя примеры позволяют нам думать, что так будет всегда), но верно для отрицательного числа:
Затем я умножаю на два с левым смещением («<< 1»), что даст нам 2 для положительного числа и 0 для отрицательного и, наконец, уменьшу на 1, чтобы получить 1 и -1 для соответственно положительных и отрицательных чисел, как этого требует ОП.
источник
Хотя целочисленное решение в принятом ответе довольно элегантно, меня беспокоило, что оно не сможет вернуть NAN для двойных типов, поэтому я немного его изменил.
Обратите внимание , что возвращение с плавающей точкой NAN , в отличии от жестких закодированных
NAN
причин знакового бита быть установлены в некоторых реализациях , поэтому выход дляval = -NAN
иval = NAN
собираешься не может быть одинаковым независимо от того , что (если вы предпочитаете «nan
» выход больше-nan
вы можете положитьabs(val)
перед возвращением ...)источник
Вы можете использовать
boost::math::sign()
метод,boost/math/special_functions/sign.hpp
если буст доступен.источник
Вот реализация для ветвления:
Если ваши данные не имеют нулей в качестве половины чисел, здесь предиктор ветвлений выберет одну из ветвей в качестве наиболее распространенной. Обе ветви включают только простые операции.
В качестве альтернативы, на некоторых компиляторах и архитектурах ЦП версия без ответвлений может быть быстрее:
Это работает для двоичного формата с плавающей точкой двойной точности IEEE 754: binary64 .
источник
Эта функция предполагает:
источник
copysign
; если вы используете,static_assert
у вас есть C ++ 11, и вы можете использовать егоcopysign
.источник
Зачем использовать троичные операторы и если-иначе, когда вы можете просто сделать это
источник
x == INT_MIN
.