signed или unsigned, вот в чем вопрос
Моя точка зрения была и пока остается такой:
Без крайней необходимости я не использую беззнаковые типы данных, т.к. это можен повлечь за собой неожиданные ошибки при вычислении выражений со смешанными типами.
У моего же оппонента точка зрения была примерно такая:
Тип данных должен соответствовать смыслу самой сущности, т.е. если у нас целочисленная координата точки на оси, то тип у нее знаковый, если длина отрезка, то беззнаковый.
В какой-то степени я с ним абсолютно согласен, но за эту красоту описания предметной области часто приходиться платить долгими поисками ошибок.
Например, такая задачка:
Есть отрезок. Дана координата одной его точки (x) и длина (length), надо найти координату середины отрезка.
const unsigned long length = 2;
const long mid = (x + x + length) / 2;
Такой код конечно же работать не будет. Необходимо делать преобразование к знаковому типу до деления, либо использовать знаковый тип для длины:
const long length = 2;
const long mid = (x + x + length) / 2;
В своей пратике подобных ошибок я допускал может и не много, но на их поиск уходило достаточно времени и, согласитесь, такие ошибки не из приятных. Они могут проявиться не сразу, а только при определенных условиях и сразу ответить на вопросы "где" и "почему" очень сложно.
Хотелось бы узнать мнение других по этому вопросу...
long mid2 = x - length / 2;
Вроде все ОК.
long mid2 = (x - length) / 2;
;)
P.S. Сколько будет, два плюс два деленное на два :)
long mid2 = x - length / 2;
Вроде все ОК.
Цель примера - показать ошибку, а не найти середину отрезка. Какая разница какая середина и куда откладывать?
P.S. Убедительная просьба постить по-существу, а не "здесь был Вася". Проблему я описал.
Так что такое с запасом? Если значение всегда неотрицательно, то использовать unsigned? Или это выбор размера типа данных? - что в данном топике не обсуждается...
А вот подход вашего друга архитектора мне кажется правильным. Безусловно из правил существуют исключения, но что же теперь из за возможности подобной ошибки отказыватся от логичности определения данных? Так не долго и от const отказатся =)
А вообще как повторял за Хоаром товарищ Кнут - "Преждевременная оптимизация ..." ну а дальше знаешь.
P.S. Убедительная просьба постить по-существу, а не "здесь был Вася". Проблему я описал.
Так об этом и речь. Чтобы избежать ошибок, надо внимательно смотреть задачу. А иначе и без unsigned можно натворить всякого.
Ну пример конечно же не правдоподобный, согласен. Но все равно, избежать выражений со смешанными типами при втором подходе будет сложно и нужно будет постоянно задумываться о возможной ошибке.
А вот подход вашего друга архитектора мне кажется правильным.
Мне он тоже кажется правильным, но на практике приводящим к некоторым неудобствам (приведение типов) и ошибкам. Возможно это из-за следующего...
Безусловно из правил существуют исключения, но что же теперь из за возможности подобной ошибки отказыватся от логичности определения данных? Так не долго и от const отказатся =)
Вот вы упомянули const и я вспомнил слова Дж. Элджера, примерно такие:
const нужно использовать либо с полной фанатичностью, либо не использовать вообще.
Может здесь и такая же проблема...
for (short i = 0; i < 50000; ++i)
arr = 1; // Oops...
В общем, по-моему самый правильный вариант - компилировать с наивысшим уровнем предупреждений. Любой нормальный компилятор при преобразовании signed/unsigned кинет warning.
Ну мне не кажется это проблемой, это плата за частичную сильную типизацию. Возможность игнорировать или убрать действия правил типизации дает определенную гибкость, но и требует зоркости от программиста =)
Использую безнаковые только в параметрах функции, чтобы предостеречь от неверного указания
Я за такойже подход...
В Jave например вообще нет беззнаковых типов.
>AndreySar
GetMid((unsigned)-10);
Вцелом любой вменяемый компилятор либо подскажет вам о автоприведении типов, либо потребует явного их указания.
Всегда использую int (System.Int32), если значимости не хватает - long (System.Int64).
Подобный выбор связан еще с тем, что любые побитовые операции происходят именно над этими типами (остальные типы имплицитно приводятся к ним).
Для вычислений, в которых целочисленные переполнения могут сыграть фатальную роль, стараюсь использовать конструкцию checked { }, а для конструкций, где значимость не важна или даже вредна - сложные операции с битами, счетчики с переполнениями и т.п. - использую unchecked { }.