Здравствуйте, Erop, Вы писали:
E>1) Вообще беззнаковая арифметика нужна в C++ в основном для того, чтобы работать со всякими странными объектами, которые на самом деле числами не являются, но от чего-то в C и С++ числами предстваленны.
Неправда. Такими словами можно выразиться только ою одном типе в С/С- типе 'unsigned char'.
E>Вот в паскале нету никакой беззнаковой арифметики и ничего.
А это уже смотря что имеется в виду под "беззнаковой арифметикой". В общем — неправда. В Паскале есть диапазонные типы. Использоание беззнаковых типов в С/С++ для представления беззнаковых величин — это как раз и есть грубый аналог использования правильно заданнного паскалевского диапазонного типа. Так что это не в Паскале чего-то нет, а как раз таки в С/С++ чего-то нет — нет диапазоннных типов. А использование беззнаковых типов в С/С++ — это лишь способ "апрроксимировать" данную паскалевскую идею.
E>Да и в математике тоже нету
Опять неправда.
Ш>>Это утверждение мне непонятно. А что гарантирует знаковость числа? E>Тоже ничего не гарантирует, кроме того, что труднее попасть в переполнение по вычитанию
И проще — по сложению. Причем ровно на столько же.
E>Но в целом в математике и вообще в мозгу нормального среднего человека числа вполне себе знаковые.
Ни в коем случае. Количественные величины "в мозгу нормального среднего человека" строго беззнаковы.
E>И в предметной области тоже занковые.
Это утверждение настолько же верно и полезно, как и утверждение, что "в предметной области" все числа — с плавающей точкой.
Здравствуйте, Аноним, Вы писали:
А>>>Я решил попробовать писать "правильный" код. Для ширины например — unsigned, т.е. она не бывает отрицательной. И тд. А>>>Но как меня всякие мелочи задолбали, извините. Чуть чуть перепутал, и всё, 4 миллиарда, вся логика валится.
C>>Нужно обращать внимание на warning’и компилятора. И иметь компилятор, который их сыплет по всякому поводу.
А>gcc, -Wall -Wextra А>варнингов нет. Да и откуда ему знать, что получится при m_width — m_pageWidth: 10 или 0 или -10
Правильность вычитания проверяется перед вычитанием. Даже в случае знаковых типов. Пытаться проверять вычитание "посмертно" — это примерно то же самое, что пытаться отлавливать деление на ноль после собственно самого деления...
Здравствуйте, Erop, Вы писали:
E>Ты просто забыл указать какой имеено из вариантов 1, 2 или 3 читабельнее, компактнее и переносимее оригинального, со знаковой арифметикой?
Читабельность... Во-первых — вещь субъективная. Во-вторых — это всегда trade-off — что-то теряем, что-то находим. Используя беззнаковый тип я средствами языка передал читателю (и компилятору) информацию о том, что даннная величина не имеет осмысленных отрицательных значений, тем самым повысив читаемость программы. А если какой-то цикл где-то и пострадал — ну так может оно того и стоило?
Компактность... Компактность не обладает самостоятельной ценностью. Разумная компактность есть лишь средство достижения читабельности. Рассмотрено выше. Или Вы о какой-то другой "компактности" говорите?
Преносимосить... Это уже что-то новенькое. Да вот и в соседнем сообщении она вдруг всплыла вместе с некоей "надежностью". На что именно Вы намекаете? Вам известны проблемы с переносимостью беззнаковых типов?
E>Ну или сойдёмся на том, что отрицательные числа придумали зануды математики, да и дело с концом.
Математики придумали много разных типов.
E>Ведь минус трёх апельсинов действительно в карман не положешь?
Да вот, я смотрю, кто-то все пытается и пытается их туда положить...
Здравствуйте, Erop, Вы писали:
E>3) Как быть без итераторов? E>Собственно я вот отказался от использования STL и его представления об итераторах и не жалею нисколько. И как-то пока ни разу не хотелось вернуться E>Я даже не против идеи итератора как такового. Только итераторы надо рожать там, где они действительно нужны, а не всюду и всегда ?) E>Просто мне кажется, что для доступа к массиву намного удобнее индекс
А я привык к указателям/итераторам, они мне импонируют больше.
E>А вообще итератор чего-то, как идея, не всегда плох. Хотя то, что ты назвал енумератором, обычно проще в реализации и не сложнее в использовании
Спрашиваю исключительно из интереса: что есть реально рабочее, умеющее красиво перебирать элементы коллекций, позволяющее писать абстрактные алгоритмы, легковесное? Вот ты какими неитераторами пользуешься?
E>Мне особенно не нравятся итераторы из STL потому что они копируют зачем-то семантику указателей. E>Вроде как все попробовали и осознали, что писать на голых указателях стрёмно. и не только потому, что не безопасно, но и потому, что непонятно E>На кой нарожали три мешка шаблонов, чтобы доступ к довольно высокоуровневым коллекциям, загнуть в такую же уродскую парадигму -- я лисно понять совершенно не могу
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Используя беззнаковый тип я средствами языка передал читателю (и компилятору) информацию о том, что даннная величина не имеет осмысленных отрицательных значений
Извините, что вмешиваюсь
Проблема в том, что до компилятора С++ это очень плохо доходит (именно в плане знаковых/беззнаковых):
int f1(int)
{
return -1;
}
unsigned f2(unsigned)
{
int i = -1;
return i;
}
unsigned f3(unsigned i, unsigned i2)
{
return i - i2;
}
int main()
{
int i = -1;
f1(i);
f2(i);
f3(1, 2);
}
msvc71 /w4 — ни одного варнинга!
И проблему усугубляет ещё то, что ведь а программист теперь-то надеется на компилятор. А куда без этого? С современными программами и темпами разработки без помощи компилятора не обойтись.
Аргументы "кривые руки" мне кажутся не совсем жизненными. По крайней мере ни одного программиста с прямыми руками я ещё не видел, и не слышал о таком.
Касательно других типов до компилятора доходит значительно лучше. Вот, например, программист повысил читабельность и сообщил компилятору, что функция возвращает только true/false (promoted to 0/1). Соответственно в такой программе получаем ожидаемый варнинг:
bool f5();
int main()
{
if (-1 == f5());
}
Например, когда я пытаюсь передать std::string как парметр int, совершенно логично программа не скомпилируется.
Фактически программист хочет ожидать, что в приведённом примере не скомпилируется вызов f2. А почему нет? Разьве это не разные типы?
Так же хотелось бы ожидать, что не скомпилируется и функция f3. По-моему это логично. Когда мы из одного числа вычитаем другое какой тип у нас получается? unsigned? Ну вообще-то по всем законам здравого смысла вроде как должен получится signed. Но в С++ почему-то не так.
Здравствуйте, remark, Вы писали:
R>Проблема в том, что до компилятора С++ это очень плохо доходит (именно в плане знаковых/беззнаковых):
Ну так может надо взять другой компилятор?
R>
R> ...
R>unsigned f2(unsigned)
R>{
R> int i = -1;
R> return i;
R>}
R>unsigned f3(unsigned i, unsigned i2)
R>{
R> return i - i2;
R>}
R>int main()
R>{
R> int i = -1;
R> f1(i);
R> f2(i);
R> f3(1, 2);
R>}
R>
R>msvc71 /w4 — ни одного варнинга!
R>Например, когда я пытаюсь передать std::string как парметр int, совершенно логично программа не скомпилируется. R>Фактически программист хочет ожидать, что в приведённом примере не скомпилируется вызов f2. А почему нет? Разьве это не разные типы?
Почему это вдруг этот вызов не должен компилироваться? Несколькими строчками выше ты говорил о предупреждениях. Опциональные предупреждения в таких ситуациях действительно стоило бы выдавать. Но "не скомпилируется"... Нет. О "не скомпилируется" не может быть и речи. Правила перобразования из int в unsigned четко определены языком и "модульное" поведение беззнаковых типов является одним из ценнейших свойств беззнаковых типов.
R>Так же хотелось бы ожидать, что не скомпилируется и функция f3. По-моему это логично. R>Когда мы из одного числа вычитаем другое какой тип у нас получается? unsigned? Ну вообще-то по всем законам здравого смысла вроде как должен получится signed.
По видимому здравых смыслов больше, чем один. Беззнаковые типы, как это уже не раз говорилось раньше, реализуют модульную арифметику. В этой модульной арифметике нет отрицательных чисел. Вычитание одного безнакового числа из другого дает по прежнему беззнаковое число. Так это работает в модульной арифметике. Если кому-то подобная арифметика кажется неестесвенной или бесполезной в программистком форуме, то я в ответ могу лишь искренне удивиться.
(Другому оппоненту: а модульную арифметику, кстати, тоже придумали математики...)
R>
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Здравствуйте, remark, Вы писали:
R>>Проблема в том, что до компилятора С++ это очень плохо доходит (именно в плане знаковых/беззнаковых):
АТ>Ну так может надо взять другой компилятор?
Зачем брать другой компилятор взамен самого распространённого промышленного компилятора???
Нет, компиляторов с++, конечно много, но меня интересует именно промышленное использование.
АТ>Почему это вдруг этот вызов не должен компилироваться? Несколькими строчками выше ты говорил о предупреждениях. Опциональные предупреждения в таких ситуациях действительно стоило бы выдавать. Но "не скомпилируется"... Нет. О "не скомпилируется" не может быть и речи. Правила перобразования из int в unsigned четко определены языком и "модульное" поведение беззнаковых типов является одним из ценнейших свойств беззнаковых типов.
Почему, не может быть и речи.
Мне кажется естественным (для типизированных языков) поведение, что когда я пытаюсь передать параметр не того типа, что нужен, программа не компилируется.
В том то и дело, что правила преобразования определены. Ну а если бы там было определено, что после выполнения "int i = 1" в i заносится 2? Тоже всё нормально?
R>>Так же хотелось бы ожидать, что не скомпилируется и функция f3. По-моему это логично. R>>Когда мы из одного числа вычитаем другое какой тип у нас получается? unsigned? Ну вообще-то по всем законам здравого смысла вроде как должен получится signed.
АТ>По видимому здравых смыслов больше, чем один. Беззнаковые типы, как это уже не раз говорилось раньше, реализуют модульную арифметику. В этой модульной арифметике нет отрицательных чисел. Вычитание одного безнакового числа из другого дает по прежнему беззнаковое число. Так это работает в модульной арифметике. Если кому-то подобная арифметика кажется неестесвенной или бесполезной в программистком форуме, то я в ответ могу лишь искренне удивиться.
Модульная арифметика, это не то, что обычно хотят люди:
Вот ответь на простой вопрос: сколько будет 1 + 1? Ну или даже так поставим вопрос: какой процент людей на земле на этот вопрос не задумываясь ответит 2? Для какого процента людей это очевидно?
Я думаю, ты согласен, что это будет близко к 100%.
Так я думаю, что ты догадываешь к чему я веду Правильный ответ, который я загадал — 0. Естественно в модульной арифметике
В целом, необходимость беззнаковой арифметики, я, конечно, отрицатать не буду. И то, что ты говорил про ОС и менеджеры памяти, я тоже согласен. Но я не согласен с "Используя беззнаковый тип я средствами языка передал читателю (и компилятору) информацию о том, что даннная величина не имеет осмысленных отрицательных значений". Это так не работает.
Т.е. я бы сформулировал так: не надо использовать беззнаковую арифметику, не надо "передавать компилятору эту дополнительную информацию", надо всегда по-умолчанию использовать int, не надо никогда в прикладном коде использовать int, и тока если после использования int сильно припрёт и человек очень хорошо понимает, что делает, можно переходить к использованию unsigned.
Но никакого "а типа вроде как количество товара не может быть отрицательным... заведу-ка я для него unsigned... ой а что это у меня получилось 4 миллиарда товаров?".
Кодт wrote:
> Пожалуйста. Пусть даны 4 числа, задающие отрезки [a;b] и [c;d]. Нужно > выяснить, какое значение — b-a или d-c — больше.
А что это означает по смыслу? Может ты хотел померить у кого длиннее? Тогда abs(b-a) < abs(d-c)
А коли так, то лучше ввести функцию:
class Line
{
int a,b; // [a,b] a<=bpublic:
Line(int a_,int b_) { if( a_<=b_ ) a=a_,b=b_; else a=b_,b=a_; }
// Нормализация хороша ещё и тем, что облегчает реализацию других операций с отрезкомunsigned length() const { return unsigned(b)-unsigned(a); }
};
int a,b,c,d;
Line(a,b).length() < Line(c,d).length()
По моему этот пример демонстрирует не мощь знаковой арифметики, а плохой стиль программирования на рассыпухе.
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Шахтер, Вы писали:
Ш>>А то что без беззнаковых типов невозможно писать надежный и переносимый код в этом и во многих других случаях. E>Ну это просто враньё
Здравствуйте, Шахтер, Вы писали:
Ш> // Нормализация хороша ещё и тем, что облегчает реализацию других операций с отрезком
А я говорил, что мне нужна длина как абсолютная величина?
Если бы это было так, то с самого начала формула b-a < d-c логически неправильна вне зависимости от типов. Тогда о чём спор, когда в программе смысловой косяк?..
Здесь другой физический смысл.
Может быть, я меряю скорость, которая может быть отрицательной величиной даже на участке пути с положительными координатами. Ну или какие-то аналогичные ситуации: приращение уровня сигнала, например.
И кстати. Что правильнее считать нормализацией — обмен координат или схлопывание в точку? (И кстати, в которую точку? первую или вторую).
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Шахтер, Вы писали:
Ш>> // Нормализация хороша ещё и тем, что облегчает реализацию других операций с отрезком
К>А я говорил, что мне нужна длина как абсолютная величина?
А, понял. Я неверно воспринял условие.
К>И кстати. Что правильнее считать нормализацией — обмен координат или схлопывание в точку? (И кстати, в которую точку? первую или вторую).
Нормализация в данном примере -- вынуждение соблюдать инвариант класса (a<=b).
Как правильно -- зависит от смысла, который ты хочешь придать выражению Line(2,1).
Вполне возможно, что в этом случае лучше всего выбрасывть исключение.
Я тоже позволю себе вмешаться в дискуссию, котора уже достойна быть помещённой в раздел "Священные войны"
Интересные аргументы приводятся "противниками" типа unsigned int, в основном всё сводится к тому, что
"ну не надо знаковую величену представлять безннаковой". И правильно, не надо. Никто этого не предлагает.
Количество предметов — беззнаковая величина, она всегда положительна, а разница в количестве может быть как положительной,
так и отрицательной. Так в чем проблема-то? Количество — unsigned int, а разница — int.
И ещё меня, допустим, удивляет реализация CArray, где метод GetSize() возвращает int, размер
массива может быть отрицательным??
Зачем брать другой компилятор взамен самого распространённого промышленного компилятора???
Нет, компиляторов с++, конечно много, но меня интересует именно промышленное использование.
Проверял в VS 2005 /W4 тоже ни одного предупреждения, но это скорее минус компилятора, а не беззнаковых типов.
Модульная арифметика, это не то, что обычно хотят люди:
Если люди — пользователи, то обычно они хотят, чтобы программа решала поставленную задачу и имела "человеческий" интерфейс. А программист должен знать типы данных языка, С++ не прощает легкомысленного программирования. (Предчувствую ответ — "давайте уволим всех ламеров")
Вот ответь на простой вопрос: сколько будет 1 + 1? Ну или даже так поставим вопрос: какой процент людей на земле на этот вопрос не задумываясь ответит 2? Для какого процента людей это очевидно?
Я думаю, ты согласен, что это будет близко к 100%.
Так я думаю, что ты догадываешь к чему я веду Правильный ответ, который я загадал — 0. Естественно в модульной арифметике
Правильный ответ — не обязательно 0, смотря по какому модулю, можно ведь взять 3, 5, 28... Если брать по модулю 2 — то 0, и это нормально, странно, если бы было по другому.
В целом, необходимость беззнаковой арифметики, я, конечно, отрицатать не буду. И то, что ты говорил про ОС и менеджеры памяти, я тоже согласен. Но я не согласен с "Используя беззнаковый тип я средствами языка передал читателю (и компилятору) информацию о том, что даннная величина не имеет осмысленных отрицательных значений". Это так не работает.
Т.е. я бы сформулировал так: не надо использовать беззнаковую арифметику, не надо "передавать компилятору эту дополнительную информацию", надо всегда по-умолчанию использовать int, не надо никогда в прикладном коде использовать int, и тока если после использования int сильно припрёт и человек очень хорошо понимает, что делает, можно переходить к использованию unsigned.
Но никакого "а типа вроде как количество товара не может быть отрицательным... заведу-ка я для него unsigned... ой а что это у меня получилось 4 миллиарда товаров?".
А что, лучше когда у нас получается -5 единиц товара? Почему странно получить 4 млрд, а -5 — нормально?
Здравствуйте, Roman Odaisky, Вы писали:
RO>Спрашиваю исключительно из интереса: что есть реально рабочее, умеющее красиво перебирать элементы коллекций, позволяющее писать абстрактные алгоритмы, легковесное? Вот ты какими неитераторами пользуешься?
Я пользуюсь библиотекой, которую ты скорее всего не знаешь. Она чем-то похожа на MFC в области коллекций. Sort там реализован, а остальное вроде как бывает нужно крайне редко.
Собственно обычно мне не нужно писать абстрактные алгоритмы.
Вернее не совсем так. Нет нужды писать настолько простые абстрактные алгоритмы, чтобы итераторы там могли как-то существенно помочь
Во всяком случае основные трудозатраты идут не на это.
Общий смысл такой, что что-то вроде двухпутевого слияния я пишу просто на C++, что-то вроде следующей перестановки мне вообще ни разу не пригодилось, и по милости Божей, мне пока удаётся так проектировтаь свои разработки, что нет нужды переходить от массива к списку или ещё куда-то, когда уже написано два вагона кода.
При этом мы таки пишем довольно сложные, и даже довольно переносимые программы, с очень сложными алгоритмами, данными, навороченными оболочками и т. п.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>(Другому оппоненту: а модульную арифметику, кстати, тоже придумали математики...)
ваша правда, товарищ эксперт, но таки когда математики думали о модульной арифметике, они как-то не ограничивали себя в выборе модуля между 0x100, 0x10000, 0x100000000 и 0x10000000000000000
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erlond, Вы писали:
E>Количество предметов — беззнаковая величина, она всегда положительна, а разница в количестве может быть как положительной, E>так и отрицательной. Так в чем проблема-то? Количество — unsigned int, а разница — int.
Хороший пример
Вот именно, что логично полагать, что разница — это int. Однако, если повнимательнее посмотреть, как работает с++, то можно удивиться. Разница между двумя количествами (unsigned) — это тоже unsigned! Не int!
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Erlond, Вы писали:
E>>Количество предметов — беззнаковая величина, она всегда положительна, а разница в количестве может быть как положительной, E>>так и отрицательной. Так в чем проблема-то? Количество — unsigned int, а разница — int.
R>Хороший пример R>Вот именно, что логично полагать, что разница — это int. Однако, если повнимательнее посмотреть, как работает с++, то можно удивиться. Разница между двумя количествами (unsigned) — это тоже unsigned! Не int!
R>
Ну почему же, всё зависит от задачи. Если разница между двумя unsigned не превышает INT_MAX, то следующий код вполне работоспособен.
unsigned int i = 3;
unsigned int j = 5;
int r = i-j;
std::cout << "r = " << r << std::endl;
Результат:
r = -2
Если же требуется получить только абсолютное значение разницы между двумя unsigned, то разница так же может быть unsigned.
P.S. Как быть с размером массива? Он тоже может быть отрицательным?
Здравствуйте, Erop, Вы писали:
E>Вот вроде как естественная нужда, сравнить у кого в апреле был прирост производительности больше и Васи или у Пети. E>Вот какая будет реализация в случае последовательно знаковых типов и какая в случае беззнаковых, там где это надо/можно?
Вот так арифметика устроена, что при сложении/вычитании двух произвольных k-битных чисел в результате получается (k+1)-битное. И независимо от знаковости приходится думать — то ли у нас значения таковы, что переполнения никогда не будет, то ли нам важна качественная картина, а младшим битом можно пожертвовать (и хранить (a+b)/2 вместо a+b), то ли вообще реализовать сложение/вычитание с насыщением.
Здравствуйте, Erlond, Вы писали:
E>Ну почему же, всё зависит от задачи. Если разница между двумя unsigned не превышает INT_MAX, то следующий код вполне работоспособен.
E>
E>unsigned int i = 3;
E>unsigned int j = 5;
E>int r = i-j;
E>std::cout << "r = " << r << std::endl;
E>
E>Результат: E>
E>r = -2
E>
Строго говоря, здесь, по 4.7.3, implementation-defined behaviour.