LINUX.ORG.RU

signed vs unsigned int in C++


0

1

Например переменная которая хранит расстояние между объектами всегда больше нуля, будет какой то плюс от использования signed int ?

П.С. про то что переменная сможет хранить в два раза большее значение знаю.



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

Помимо указанного тобой — никаких. Ну и не придётся проверять значения на не отрицательность.

KblCb ★★★★★
()

С signed int ты не наступишь на грабли, когде тебе захочется посчитать разность расстояний и она вдруг окажется отрицательной. С unsigned int — запросто.

Deleted
()

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

x86-плюсы: 1) из интеловских мануалов следует, что ja/jb эффективнее jg/jl, поэтому циклы вида for(unsigned int i = 0; i < N; i++) будут выполняться чуть быстрее знаковых вариантов. 2) idiv — одна из самых тормозных операций, imul тоже не подарок => умножение/деление беззнаковых чисел пошустрее будет.

anonymous
()

signed int можно проверить на отрицательность, unsigned - нет (труднее заметить ошибки)

anonymous
()

Например переменная которая хранит расстояние между объектами всегда больше нуля, будет какой то плюс от использования signed int ?

если у тебя в программе signed/unsigned, то компилятор будет сыпать предупреждения на арифметике. В принципе это даже хорошо, но надо писать всегда так:

if(расстояние <= 0u)

помогает для защиты от сравнения солёного с мягким.

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

for(unsigned int i = 0; i < N; i++) будут выполняться чуть быстрее знаковых вариантов.

неправда. Компилятор без тебя это знает, и в 95% выкрутится. Например будет считать не 1+2+3+4+5+6+7, а 7+6+5+4+3+2+1. Т.к. знает, что dec ebc; jnz по любому быстрее inc ebc; cmp ebc, N, jl.

2) idiv — одна из самых тормозных операций

тоже не правда ваша, div не лучше, и компилятор не любит делений вообще. Опытные программисты их кстати тоже не любят. Особенно в целых, где результат мало того что не точный(и сильно!), дык ещё и программа может вообще рухнуть нафиг.

imul тоже не подарок

ага. Особенно в сишечке, в которой ты теряешь половину результата(причём без разницы хзоть со знаком, хоть без знака, причём не просто половину, а старшую, самую важную).

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

unsigned тоже можно. Чем нить типа >((~0u)>>1).

Но самое главное, никто не мешает тебе проверять ДО, например вот так можно посчитать расстояние между x, y: x>y ? x-y : y-x;

Со знаковыми тебе всё равно придётся считать модуль, т.ч. то на то и выйдет (после оптимизации), только с unsigned точнее.

emulek
()

переменная которая хранит расстояние между объектами

От float/double ты отказался потому, что...

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

разность расстояний и она вдруг окажется отрицательной. С unsigned int — запросто.

Запросто что? Точно также окажется отрицательной, товарищь капитан? Или запросто получит варнинг присваивая переменной значение, при том, что выражение компилятор расшряет до signed int?

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

float/double ты отказался потому, что...

Потому что мне нужно всего лишь определять может ли один объект стрелять по другому. Зачем тут float?

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

Потому что мне нужно всего лишь определять может ли один объект стрелять по другому. Зачем тут float?

потому что float в разы быстрее, занимают те же 4 байта, и не переполняются, в отличие от. Ещё можно делить на ноль(т.е. в программе не нужно возиться с Over9000 проверками).

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

А зачем тут int? Или может или нет. Один бит.

if(dist < 100){
something.fire();
}
knotri
() автор топика
Ответ на: комментарий от knotri

У меня двухмерная система измерений. считаю так x*x+y*y

ну тем более. с 4х байтовыми int тебе надо постоянно следить, что-бы x,y были НИКОГДА не больше 32768. Зачем тебе этот геморрой?

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

Запросто что? Точно также окажется отрицательной, товарищь капитан? Или запросто получит варнинг присваивая переменной значение, при том, что выражение компилятор расшряет до signed int?

Запросто наступит на грабли, когда никакого расширения типа до signed нет:

if (a.dist - b.dist < 10) {
    /* something */
}
Deleted
()
Ответ на: комментарий от knotri

А счетчик циклов?

не забивайте голову ерундой, int. Это как раз с unsigned можно поиметь *реальных* проблем с быстродействием, если его тупо НЕТ, а вы вынуждаете компилятор его костылить. И вообще, преждевременная оптимизация == зло.

size_t

это ещё зачем?

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

Если индексируешь что-то чужое, то расово будет [ s]size_t

//fixed

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

Затем, что int в теории может быть 32 битным, а кусок памяти char * больше 4х гигабайт, например. Так что везде где есть buf[ i], i должен быть size_t. strlen именно его возвращает, если внимательно присмотреться :)

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

Затем, что int в теории может быть 32 битны

он и на практике 32х битный.

а кусок памяти char * больше 4х гигабайт, например.

в сферическом вакууме — может, в реальном приложении — мало вероятно. Мало вероятно, что у тебя ВНЕЗАПНО получаться char*'ы больше 4Гб.

size_t. strlen именно его возвращает, если внимательно присмотреться :)

дык strlen(3) предназначена для _любых_ строк. Я и говорю: если ты пишешь какую-то либу, то size_t — разумный выбор, но если у тебя свой helloworld, то на кой ляд там size_t — непонятно.

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

Затем, что int в теории может быть 32 битным, а кусок памяти char * больше 4х гигабайт, например.

По факту почти никогда не бывает больше 32х бит.

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

он и на практике 32х битный.

Опасная таки практика — так обще утверждать.

Мало вероятно, что у тебя ВНЕЗАПНО получаться char*'ы больше 4Гб.

Нельзя же строить код на «маловероятно», когда это может быть нормальным сценарием и по стандарту и на десктопе. Тот же mmap может замапить hugefile как uint8_t * и бегай по нему сколько влезет..

Для хелловорлда согласен, но почему сразу не привыкать писать нормальный код? Ты же не хелловорлды писать собрался по жизни, я еще понимаю, если в крестах холивары как правильно себе на ботинок нассать, а тут естественные размерности не соблюдать — это фэйл. Вы б еще разницу указателей в int клали.

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

Я и говорю — новый миллениум, скоро в каждом зрачке будет по интернету, а мы все размерности путаем :)

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

он и на практике 32х битный.

Опасная таки практика — так обще утверждать.

где ты увидел слово «всегда» и слово «везде»? Хватит додумывать! «На практике» == здесь и сейчас на моих локалхостах.

Нельзя же строить код на «маловероятно»

я и не строю. Как раз наоборот — код должен правильно работать с _любыми_ int'ами. Завтра они станут 64х битными, дык юзеры этого даже не заметят, просто для них массивы в Over9000Mb станут обычным делом, как и 64х битный int.

Для хелловорлда согласен, но почему сразу не привыкать писать нормальный код?

«нормальный код» это у Кнута, который хоть на 6и битных байтах работал, хоть на десятичных от 0 до 100. Вот это — Ъ. А у нас — говно.

Вы б еще разницу указателей в int клали.

для разницы указателей есть ptrdiff_t, а никак НЕ size_t. Разницу надеюсь знаешь?

emulek
()

Бинарные и унарные операции над числами в C/C++ есть прямой маппинг из математики. Беззнаковые операции нарушают математическую семантику, ибо отнимая от нуля единицу всегда должна получиться минус единица. Если переменная явно или косвенно учавствует в математических операциях, она обязана быть знаковой. По этой же причине в современных языках типа Java, Python, JS беззнаковых чисел нет вообще.

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

Компилятор без тебя это знает, и в 95% выкрутится. Например будет считать не 1+2+3+4+5+6+7, а 7+6+5+4+3+2+1. Т.к. знает, что dec ebc; jnz по любому быстрее inc ebc; cmp ebc, N, jl.

Во-первых, jb быстрее jl. Во-вторых, умник, ты бы посмотрел для начала, какой код генерирует компилятор: если ты используешь i внутри цикла для чего-то отличного от проверки на «<N», то компилятор не имеет права поступать подобным образом.

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

в современных языках типа Java, Python, JS беззнаковых чисел нет вообще.

Потому что это современные языки для современных программистов, которые привыкли звать split, чтобы получить часть строки до первой зяпятой - преобразование signed <=> unsigned для их неокрепшего межушного нервного ганглия черезчур тяжело.

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

Завтра они станут 64х битными, дык юзеры этого даже не заметят, просто для них массивы в Over9000Mb станут обычным делом, как и 64х битный int.

Я и предлагаю ориентироваться на предназначенные типы, а не на ветреную моду на размер инта.

«нормальный код» это у Кнута, который хоть на 6и битных байтах работал, хоть на десятичных от 0 до 100. Вот это — Ъ. А у нас — говно.

Это у вас может там где-то у кого-то говно, а я всегда стараюсь «как у Кнута». В плане рамерности достаточно использовать предложенные лимиты и ориентироваться на стандартовые аксиомы о размерах. Вот ТЫ только не начинай про царственно-недостижимую мудрость отцов, а? Это же инженерная область, блджад.

для разницы указателей есть ptrdiff_t, а никак НЕ size_t. Разницу надеюсь знаешь?

Тебя проверял конечно же. Хватит додумывать, не такая уж я анскильная лалка, в конце-то концов, док!

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

И щито же происходит при сложении двух достаточно больших чисел в этой вашей яве? Без бигнума, естественно.

Даю подсказку: «математика» бывает циркулярной (это как все привыкли), сатурационной (это как некоторые команды sse делают), и еще б-г весть какие приграничные правила можно придумать для частных случаев, как программных, так и аппаратных. Даже минус один можно по-разному представлять, представляете?

прямой маппинг из математики

Тащемта прямой маппинг из инструкций cpu и их реализации, например. Математикой там и не пахло, как и определенностью стандартом. Тот же integer overflow та еще радость. А сдвиги-то, сдвиги!

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

Анончик, а почему jb быстрее jl? Со знаком оп-вывод долгий что-ли, или знак барьер втыкает? Чем так принципиально отличается?

По-моему вся разница CF и SF только в том, то CF стреляет в нуле, а SF в 0x8000000… Разве нет?

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

Беззнаковые операции нарушают математическую семантику, ибо отнимая от нуля единицу всегда должна получиться минус единица.

натуральные числа? Не, не слышал. Это вообще не математика, а какая-то ересь!

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

Во-первых, jb быстрее jl.

а jnz без cmp ещё быстрее. Смирись.

Во-вторых, умник, ты бы посмотрел для начала, какой код генерирует компилятор: если ты используешь i внутри цикла для чего-то отличного от проверки на «<N», то компилятор не имеет права поступать подобным образом.

да ладно. С какого перепугу?

у меня оно вообще просто выкинуло напрочь цикл с O3, а с O2 заменило <N на cmp; jnz

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

Это у вас может там где-то у кого-то говно, а я всегда стараюсь «как у Кнута». В плане рамерности достаточно использовать предложенные лимиты и ориентироваться на стандартовые аксиомы о размерах. Вот ТЫ только не начинай про царственно-недостижимую мудрость отцов, а? Это же инженерная область, блджад.

ну лично я не вижу смысла для какого-то массива в 100 чисел писать size_t. ИМХО int обычно достаточно, кроме каких-то особых случаев. А уж ТСу-то точно за глаза (хотя я-бы на его месте юзал float)

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

Тащемта прямой маппинг из инструкций cpu и их реализации, например. Математикой там и не пахло, как и определенностью стандартом

нормальная там математика, только не та, что в школе учат. Впрочем Dendy наверняка и школьной-то не знает, можешь для прикола спросить у него про знак остатка от деления ☺

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

а почему jb быстрее jl

да была какая-то такая багофича на i80186 или на чём-то подобном… Во времена седой древности.

По-моему вся разница CF и SF

там ещё и переполнение бывает. С целыми жеж беда — они кривые и перекошенные: в char'ах есть -128, а вот +128 не бывает. Т.е. ты не можешь вычислить 0-(-128), получается переполнение. Видать древние CPU микрокодом как-то по особому обрабатывали эту особую ситуацию, потому и медленнее.

Хотя я уже и не помню точно в чём там беда…

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

Так тогда сравнение будет медленнее, а не сам переход. JL в OF не заглядывает. Буду ждать анона, пусть проясняет.

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

AFAIK это абсолютно не играет никакой роли, из-за того, что CPU предсказывает переход, и в зависимости от предсказания соответствующим образом организует конвейер. Для предсказания используются эвристики, вроде той, что переход назад обычно выполняется(а вперёд обычно нет), и также что если переход выполнился три раза, то он выполнится и в четвёртый. А всё это «заглядывание» сейчас настолько аппаратное, что времени занимает одинаково, только транзисторов на него уходит побольше.

Т.е. намного большие тормоза возникнут не в случае jl, а в случае, когда CPU неправильно предсказал переход, и часть конвейера пошла псу под хвост.

И да, если я правильно путаю, то ещё во времена первопня cmp спаривалась одинаково хорошо что с jnz, что с jl, и занимала такая спарка по времени 1 такт. Т.е. разница была намного раньше первопня, когда CPU выполнял команды за несколько тактов, и по одной.

PS: да, я уже тыщу лет тактами не обмазывался, могу ошибиться.

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

это вброс или на самом деле?

как обычно у меня: и то и другое ☺

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

Это глупая чушь.

хм…

Даже в тривиальном тесте

#include <stdio.h>

int f1(int a, int b)
{
	return a==b ? 1 : (a+b)/(a-b);
}

float f2(float a, float b)
{
	return a==b ? 1 : (a+b)/(a-b);
}

int main()
{
	int j, s;
	for(j = s = 0; j < 1024*1024*1024; j++)
		s += f2(j, 17);
	printf("%d\n", s);
	return 0;
}

получается для float

real	0m9.170s
user	0m4.121s
sys	0m0.004s

для int

real	0m8.063s
user	0m3.743s
sys	0m0.004s

как видишь, разница не очень большая(и она, кстати, сильно зависит от CPU, это был intel g2020).

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

хм…

Да, ты обоссалась.

Даже в тривиальном тесте

Это не тест - это говно.

a==b ? 1 :

Ты это зачем сюда влупил, обсосок - это не имеет никакого смысла, ущербан.

(a+b)/(a-b)

Ущербан бенчит деление. Ты реально настолько туп, что не понимаешь - нет смысла бенчить деление, ибо деление на твоём говне стоит тактов 100. На фоне 100тактов на деление ну никак не праявляются оверхды, которые стоял максимум десятки тактов и то, если у тебя такая разница даже на делени, то на умнождении там будут разы.

И да обсосинка, замени деление на умножение. Посмотришь сколько стоит каст на твоём говне.

g2020

Ну я же говорил, что нищая обсосина - тыж говорил, что «бенчить на говне не имеет смысла», а тут ты мало того, что бенчишь, дак ещё кукарекаешь о других цпу, которые ты в глаза не видел.

и она, кстати, сильно зависит от CPU

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

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

Это ущербанище говорит херню во всех случаях, собственно сам он и обосрался на своих тестах.

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