LINUX.ORG.RU

Знак переменной

 , ,


0

2

Есть ли кроссплатформенный способ определить знак переменной, лучше этого:

T val = get_random(-max, max);
T sign = (val > 0) - (val < 0);

★★★★★

Последнее исправление: andreyu (всего исправлений: 1)

1 - 2*(val < 0)

Хорош тем, что val используется один раз и можно функции использовать (один раз вычисляться будут).

ziemin ★★
()
Ответ на: комментарий от ziemin

Хорош тем, что val используется один раз и можно функции использовать (один раз вычисляться будут).

Но плох тем, что результат только -1 или 1. А нужно еще и 0.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от unanimous

А переменная какого типа, кстати?

В моем случае float, но хотелось бы универсального решения. Платформо-зависимая магия не подходит.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от d

copysign

Я же просил решение лучше, чем описанное в топике.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от andreyu

А можно поинтересоваться, где нужно такое? Если нужно выполнять различные действия, то в любом случае потом придётся делать несколько if'ов, в таком случае можно просто сразу проверять знак

if (val < 0) {
...
} else if (val > 0) {
...
} else {
...
}

А тебе хочется именно сохранить в переменную, а не выполнять действия в зависимости от знака.

KivApple ★★★★★
()
Ответ на: комментарий от andreyu

Тут тоже только два значения - или "-", или «+», а «0» нет.

Ну так допиши специальный случай для нуля. И по какому параметру способ должен быть лучше?

P.S. http://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-...

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от KivApple

А можно поинтересоваться, где нужно такое? Если нужно выполнять различные действия, то в любом случае потом придётся делать несколько if'ов, в таком случае можно просто сразу проверять знак

Нет, не обязательно потом задействовать ифы. Результат операции можно использовать в вычислениях или как индекс на указатель на функцию. Например

T new_speed = speed * sign; // new_speed: -speed, 0, speed

или

size_t idx = 1 + sign; // idx = 0, 1, 2

andreyu ★★★★★
() автор топика
Ответ на: комментарий от hateyoufeel

Ну так допиши специальный случай для нуля. И по какому параметру способ должен быть лучше?

Специальный способ - это еще одно условие. Вы прочли мой начальный пост?

andreyu ★★★★★
() автор топика
Ответ на: комментарий от andreyu

Походу вы не читали ни мой вариант, ни то, что написано по линку.

По ссылки под вариантом, аналогичным твоему, есть такое: (x < 0) ? -1 : (x > 0).

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

По ссылки под вариантом, аналогичным твоему, есть такое: (x < 0) ? -1 : (x > 0).

Это два равнозначных варианта.

andreyu ★★★★★
() автор топика

Есть ли кроссплатформенный способ определить знак переменной, лучше этого

По каким критериям лучше? Если по производительности, то можно просто взять и посмотреть. Например, для x86 получаем:

/tmp/1$ cat 1.c


int signi(int val) {
	return (val > 0) - (val < 0);
}

double signd(double val) {
	return (val > 0) - (val < 0);
}

/tmp/1$ gcc -O3 -S 1.c 
/tmp/1$ cat 1.s
	.file	"1.c"
	.section	.text.unlikely,"ax",@progbits
.LCOLDB0:
	.text
.LHOTB0:
	.p2align 4,,15
	.globl	signi
	.type	signi, @function
signi:
.LFB0:
	.cfi_startproc
	movl	4(%esp), %edx
	xorl	%eax, %eax
	testl	%edx, %edx
	setg	%al
	shrl	$31, %edx
	subl	%edx, %eax
	ret
	.cfi_endproc
.LFE0:
	.size	signi, .-signi
	.section	.text.unlikely
.LCOLDE0:
	.text
.LHOTE0:
	.section	.text.unlikely
.LCOLDB3:
	.text
.LHOTB3:
	.p2align 4,,15
	.globl	signd
	.type	signd, @function
signd:
.LFB1:
	.cfi_startproc
	subl	$12, %esp
	.cfi_def_cfa_offset 16
	xorl	%eax, %eax
	fldl	16(%esp)
	fldz
	fxch	%st(1)
	fucomi	%st(1), %st
	fxch	%st(1)
	seta	%al
	xorl	%edx, %edx
	fucomip	%st(1), %st
	fstp	%st(0)
	seta	%dl
	subl	%edx, %eax
	movl	%eax, 4(%esp)
	fildl	4(%esp)
	addl	$12, %esp
	.cfi_def_cfa_offset 4
	ret
	.cfi_endproc
.LFE1:
	.size	signd, .-signd
	.section	.text.unlikely
.LCOLDE3:
	.text
.LHOTE3:
	.ident	"GCC: (GNU) 4.9.2 20141224 (prerelease)"
	.section	.note.GNU-stack,"",@progbits

Как видно, gcc достаточно умён, чтобы сгенерировать для целочисленной функции оптимальный код без условных переходов.

Алгоритм для double не берусь оценить, для меня система команд FPU x86 — китайская грамота.

Deleted
()

Этот способ тоже платформозависим. Мне встречались случаи, когда истина кодировалась не 1 а -1, т.е числом, у которого все биты установлены. А еще, теоритически, операции > и < могут возвращать соответсьвенно на сколько он больше или меньше. Истинно кросплатформенно только с двумя проверками. А еще можно завернуть в функцию и платформозависимую часть делать условной компиляцией.

German_1984 ★★
()
Ответ на: комментарий от German_1984

Мне встречались случаи, когда истина кодировалась не 1 а -1, т.е числом, у которого все биты установлены. А еще, теоритически, операции > и < могут возвращать соответсьвенно на сколько он больше или меньше.

Выбросьте ваш компилятор.

Each of the operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) shall yield 1 if the specified relation is true and 0 if it is false. The result has type int.

Deleted
()
Ответ на: комментарий от German_1984

Этот способ тоже платформозависим. Мне встречались случаи, когда истина кодировалась не 1 а -1, т.е числом, у которого все биты установлены. А еще, теоритически, операции > и < могут возвращать соответсьвенно на сколько он больше или меньше.

Ну и что? Тут два неявных преобразования типа - в скобках результат приведется к bool, а потом приведется к T.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от Deleted

Как видно, gcc достаточно умён, чтобы сгенерировать для целочисленной функции оптимальный код без условных переходов.

Неплохо, спасибо.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от Deleted

Алгоритм для double не берусь оценить, для меня система команд FPU x86 — китайская грамота.

сейчас у нас SSE, а не FPU

emulek
()
Ответ на: комментарий от emulek

сейчас у нас SSE, а не FPU

Это на x86_64 «сейчас у нас» SSE. А на x86 ABI аргументы-флоаты передаются в регистрах FPU, и там же и вычисления gcc провёл.

Deleted
()
Ответ на: комментарий от emulek

fabs

Тады уж сразу

#define signum(x)  (x < 0 ? -1 : 1)

if(signum(x) == -1)
  ; // minus
else
  ; // plus

Потому как нафига макрос fabs, который раскрывается в (x > 0 ? x : -x) лепить?

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Deleted

Выбросьте ваш компилятор.

Each of the operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) shall yield 1 if the specified relation is true and 0 if it is false. The result has type int.

Тот компилятор я давно выбросил, это было до 1999 года.

А не напомните что говорит стандарт C++ о преобразовании bool в int или double?

German_1984 ★★
()
Ответ на: комментарий от Deleted

Это на x86_64 «сейчас у нас» SSE. А на x86

IA-32 сейчас практически вышло из применения. Годно только как legacy. Т.е. мой код конечно собирается на IA-32(я проверяю), но на производительность мне наплевать.

emulek
()
Ответ на: комментарий от Eddy_Em

Потому как нафига макрос fabs, который раскрывается в (x > 0 ? x : -x) лепить?

Эдди, вопрос был за «кроссплатформенный способ». А макрос fabs (на самом деле это не обязательно макрос) вполне стандартный. А нестандартные макросы == грабли.

emulek
()
Ответ на: комментарий от German_1984

А не напомните что говорит стандарт C++ о преобразовании bool в int или double?

уже обсасывали на 20и страницах. ЕМНИП true→1, false→0.

emulek
()
Ответ на: комментарий от andreyu

и да, это троллинг был.

На самом деле ты делаешь х-ню: вот зачем тебе «индекс»? Что-бы оптимизировать, да? Но всё равно тебе придётся сравнивать, и переводить в 0,1,2. Просто возьми, и сравни:

if(x<0)
  …;
else if(x==0)
  …;// only for integer
else
  …;

И извращаться тут не нужно. Компилятор не распарсит, и сгенерирует более медленный код.

emulek
()
Ответ на: комментарий от emulek

На самом деле ты делаешь х-ню: вот зачем тебе «индекс»? Что-бы оптимизировать, да? Но всё равно тебе придётся сравнивать, и переводить в 0,1,2.

Ты про branchless code слышал когда-нибудь?

Каждый неправильно предсказанный джамп — полный перезапуск конвеера. Там, где сильно критична производительность арифметики, выкидывают все переходы насколько возможно.

Компилятор не распарсит, и сгенерирует более медленный код.

На практике вычисление индекса и его последующее использование в вычислениях зачастую оказываются быстрее блока if else if.

Deleted
()
Ответ на: комментарий от German_1984

If the source type is bool, the value false is converted to zero and the value true is converted to one.

Deleted
()
Ответ на: комментарий от Eddy_Em

у float нет нуля.

есть, даже два: +0 и -0. Это особое значение вроде ±INF. И как любое особое значение, его можно прочитать из флагов FPU. Вот только я не помню, есть-ли стандартный способ читать эти флаги.

И чем тебе не нравится (x < 0. ? -1 : 1)?

тем, что это скрытое ветвление, которое компилятор не распарсит, и будет на нём тормозить. Да и вообще, макросы == зло.

emulek
()
Ответ на: комментарий от Deleted

Можешь проверить во что скомпилируется вот это?


(~((x >> 31) & 1) + 1) | (((~x + 1) >> 31) & 1)

Ато у меня компилятора ссобой нет.

PS: парсер лох, тег code сломан

unt1tled ★★★★
()
Последнее исправление: unt1tled (всего исправлений: 1)

(val > 0) - (val < 0);

Кстати, если требуется для флоатов корректно обрабатывать NaN, то код ошибочный.

Deleted
()
Ответ на: комментарий от Deleted

Каждый неправильно предсказанный джамп — полный перезапуск конвеера. Там, где сильно критична производительность арифметики, выкидывают все переходы насколько возможно.

предлагаешь запихать ветвление в макрос, и потом использовать его в каждой строчки по три раза?

На практике вычисление индекса и его последующее использование в вычислениях зачастую оказываются быстрее блока if else if.

на практике блок if else компилятор сам может выкинуть, и выкидывает. А вот если ты ветвление запихал в вычисление индекса, неявно, компилятор это ветвление выкинуть не может. Особенно НЕ радует ноль. Эдди дело говорит, у float нулей нет, это особый случай. Но ты ведь всё равно пихаешь дополнительное ветвление, на случай нуля, которого нет. И компилятор вынужден включать в код эту не нужную проверку.

И вообще, такие примитивные оптимизации известны не только тебе, но и разработчикам компилятора. Будет лучше, если ты займёшься алгоритмом, а не ветвлениями. С ветвлениями компилятор справится лучше тебя, т.к. знает целевой процессор, а ты — не знаешь (или ты одмин локалхоста?). С алгоритмом компилятор справится пока не в состоянии, и заниматься алгоритмом придётся тебе.

ЗЫЖ «выкидывать переходы» тоже надо с умом. Многие переходы отлично предсказываются, и альтернативный вариант оказывается медленнее. Также надо помнить о том, что кеш L1 очень небольшой, и если код туда не влезет, производительность упадёт ниже плинтуса. Т.ч. раздувать код убирая ветвления нужно очень осторожно, что-бы не вылезти из L1.

emulek
()
Последнее исправление: emulek (всего исправлений: 1)
Ответ на: комментарий от unt1tled

Можешь проверить во что скомпилируется вот это?

тебе amd64 или IA-32?

emulek
()
Ответ на: комментарий от unt1tled
        movl    4(%esp), %edx
        movl    %edx, %eax
        sarl    $31, %edx
        negl    %eax
        shrl    $31, %eax
        orl     %edx, %eax
        ret
Deleted
()
Ответ на: комментарий от German_1984

Мне встречались случаи, когда истина кодировалась не 1 а -1, т.е числом,

А что вас не устраивает? -1 это не ноль, так что все правильно.

RiseOfDeath ★★★★
()
Ответ на: комментарий от emulek

предлагаешь запихать ветвление в макрос

Предлагаю включить голову.

Процедуры для branchless вычислений оформляются в хидер как static inline и используются.

и потом использовать его в каждой строчки по три раза?

Моя твоя русская языка не понимай.

на практике блок if else компилятор сам может выкинуть, и выкидывает. А вот если ты ветвление запихал в вычисление индекса, неявно, компилятор это ветвление выкинуть не может.

Ага, очень сложно выкинуть ветвление, которого нет. Лол.

Даю установку, повторяй за мной: код без ветвлений, код без ветвлений, код без ветвлений.

...

Далее пошел бред в связи с неспособностью emulek-а понять слова «код без ветвлений».

Deleted
()
Ответ на: комментарий от Deleted

покажи «свой код», который кросплатформенно и без ветвлений обрабатывает знак хрен знает какого типа.

emulek
()
Ответ на: комментарий от Deleted

и да, если ты получил signum без ветвлений, и используешь индекс, то дальше у тебя будет косвенный jump по таблице. Это по твоему быстрее, чем ветвление? (учитывая что вариантов у нас 2).

emulek
()
Ответ на: комментарий от RiseOfDeath

In C++, a 0 value is automatically converted into false, and a non-zero value is automatically converted into true.

facepalm

ты спросил ВО ЧТО, а не ИЗ ЧЕГО. Читай дальше стандарт, и не тупи.

emulek
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.