Макрос RAND_MAX: подписан или без знака?

Я искал стандарт C (с 1999 года), и он говорит только, что RAND_MAX должен быть не менее 32767, но ничего не говорит о том, должен ли этот макрос расширяться до подписанного или беззнакового int. Спецификация Single UNIX ( ссылка 1 , ссылка 2 ) и Linux man ( ссылка ) не добавляют ясности.

Казалось бы, RAND_MAX должен быть signed int так как это возвращает rand() .

Тем не менее, я обнаружил, что некоторые компиляторы определяют его как unsigned:

  • Древний Turbo C ++ 1.01: #define RAND_MAX 0x7FFFU
  • Не очень древний C ++ Builder 5.5: #define RAND_MAX 0x7FFFU
  • Все еще живой Open Watcom C / 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
  • Цифровой Марс C / C ++ 8.52: #define RAND_MAX 32767

Это делает, казалось бы, безобидный код, такой как ниже, стать не переносным и взорваться из-за подписания без подписи:

 cos(w * t) + (rand() - RAND_MAX / 2) * 0.1 / (RAND_MAX / 2); 

rand() возвращает signed int в диапазоне [0, RAND_MAX ].

Если RAND_MAX определяется как unsigned int , значение из rand() получает значение unsigned int .

И если это так, разница (rand() - RAND_MAX / 2) становится беззнаковым разностью беззнаковых целых чисел со значением в диапазонах [0, RAND_MAXRAND_MAX / 2] и [ UINT_MAX + 1- RAND_MAX / 2, UINT_MAX -1] вместо того, чтобы быть подписанной разностью знаковых целых чисел со значением в диапазоне [- RAND_MAX / 2, RAND_MAXRAND_MAX / 2].

Во всяком случае, похоже, что RAND_MAX должен быть подписан, и большинство (?) Компиляторов определяют его как таковое, но есть ли авторитетный источник, который говорит, что он должен быть подписан? Старые стандарты? К & R? Еще одна спецификация UNIX?

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

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

Да, это похоже на дефект в стандарте.

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

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

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