Макрос RAND_MAX: подписан или без знака?
Я просмотрел стандарт C (с 1999 года), и он говорит только, что RAND_MAX
должен быть не менее 32767, но ничего не говорит о том, должен ли этот макрос расширяться до подписанного или беззнакового int. Спецификация Single UNIX (ссылка 1, ссылка 2) и Linux man (ссылка) не добавляют ясности.
Казалось бы, RAND_MAX
должен быть signed int
, так как возвращается rand()
.
Однако я обнаружил, что некоторые компиляторы определяют его как unsigned:
- Древний Turbo С++ 1.01: #define RAND_MAX 0x7FFFU
- Не так древний С++ Builder 5.5: #define RAND_MAX 0x7FFFU
- Все еще жив Open Watcom C/С++ 1.9: #define RAND_MAX 32767U
- DJGPP (gcc 3.3.4 для DOS): #define RAND_MAX 2147483647
- MinGW (gcc 4.6.2 для Windows): #define RAND_MAX 0x7FFF
- MS Visual Studio 2010 (ссылка): RAND_MAX определяется как значение 0x7fff
- Tiny C Compiler 0.9.25: #define RAND_MAX 0x7FFF
- lcc-win32 3.8: #define RAND_MAX 0x7fff
- Pelles C 6.50: #define RAND_MAX 0x3fffffff ИЛИ #define RAND_MAX 0x7fff
- Digital Mars C/С++ 8.52: #define RAND_MAX 32767
Это делает, казалось бы, безобидный код, похожий на ниже, стать не переносимым и взорваться из-за подписания без подписи:
cos(w * t) + (rand() - RAND_MAX / 2) * 0.1 / (RAND_MAX / 2);
rand()
возвращает a signed int
в диапазоне [0, RAND_MAX
].
Если RAND_MAX
определяется как unsigned int
, значение из rand()
также увеличивается до unsigned int
.
И если в этом случае разность (rand() - RAND_MAX / 2)
становится беззнаковым разностью беззнаковых целых чисел со значением в диапазонах [0, RAND_MAX
- RAND_MAX
/2] и [UINT_MAX
+ 1- RAND_MAX
/2, UINT_MAX
-1] вместо того, чтобы быть знаковой разностью знаковых целых чисел со значением в диапазоне [- RAND_MAX
/2, RAND_MAX
- RAND_MAX
/2].
Во всяком случае, похоже, что RAND_MAX
должен быть подписан, и большинство (?) компиляторов определяют его как таковое, но есть ли какой-либо авторитетный источник, который говорит, что он должен быть подписан? Старые стандарты? К & усилителя, R? Еще одна спецификация UNIX?
Ответы
Ответ 1
Ответ: этот код делает необоснованное предположение, поэтому его необходимо устранить. То, что должен был сделать этот код, заключается в использовании RAND_MAX
- (int)
при использовании.
Хотя для компиляторов было бы более разумно определять макросы RAND_MAX
как подписанные, стандарт тщательно избегает их требовать. Это делает его ошибкой переносимости для любого кода, чтобы слепо предположить, что он будет подписан.
Ответ 2
Да, это выглядит как дефект в стандарте.
Во-первых, в настоящее время, вероятно, никто бы не определил rand()
, чтобы вернуть int
. Цель состоит в том, чтобы вернуть положительное число, и нет возврата ошибки, который мог бы использовать отрицательные результаты. Такая функция, если она была введена сегодня, должна быть сконструирована с целым типом без знака в качестве типа возврата. Я предполагаю, что это предшествует C89 и введение понятия целых чисел без знака в язык.
Тогда, очевидно, следует ожидать, что определение максимального значения, которое возвращает функция, имеет тот же тип, что и функция. В других местах макросы хорошо определены как расширяющиеся к выражениям определенного типа, поэтому это было бы возможно и здесь.
Что касается вашей проблемы, я думаю, что проще всего иметь что-то переносное, чтобы сначала масштабировать значение до двойника в [0, 1)
, выполняя что-то вроде rand()/(RAND_MAX+1.0)
и выводить из него все ваши вычисления. В любом случае вы должны использовать rand()
только в крайнем случае, если ваша платформа не имеет лучшего псевдослучайного генератора. Например, в системах POSIX семейство rand48
является удобной заменой хорошо описанных свойств.