Подтвердить что ты не робот

Макрос 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?

4b9b3361

Ответ 1

Ответ: этот код делает необоснованное предположение, поэтому его необходимо устранить. То, что должен был сделать этот код, заключается в использовании RAND_MAX - (int) при использовании.

Хотя для компиляторов было бы более разумно определять макросы RAND_MAX как подписанные, стандарт тщательно избегает их требовать. Это делает его ошибкой переносимости для любого кода, чтобы слепо предположить, что он будет подписан.

Ответ 2

Да, это выглядит как дефект в стандарте.

Во-первых, в настоящее время, вероятно, никто бы не определил rand(), чтобы вернуть int. Цель состоит в том, чтобы вернуть положительное число, и нет возврата ошибки, который мог бы использовать отрицательные результаты. Такая функция, если она была введена сегодня, должна быть сконструирована с целым типом без знака в качестве типа возврата. Я предполагаю, что это предшествует C89 и введение понятия целых чисел без знака в язык.

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

Что касается вашей проблемы, я думаю, что проще всего иметь что-то переносное, чтобы сначала масштабировать значение до двойника в [0, 1), выполняя что-то вроде rand()/(RAND_MAX+1.0) и выводить из него все ваши вычисления. В любом случае вы должны использовать rand() только в крайнем случае, если ваша платформа не имеет лучшего псевдослучайного генератора. Например, в системах POSIX семейство rand48 является удобной заменой хорошо описанных свойств.