RAND_MAX macro: ¿firmado o sin firmar?

Busqué el estándar C (de 1999) y solo dice que RAND_MAX debería tener al menos 32767, pero no dice si esta macro debería expandirse a un int firmado o sin firmar. La Especificación Única de UNIX ( enlace 1 , enlace 2 ) y Linux man ( enlace ) no agregan claridad.

Uno podría pensar que RAND_MAX debería ser un signed int ya que eso es lo que rand() devuelve.

Sin embargo, he encontrado que algunos comstackdores lo definen como sin firmar:

  • El antiguo Turbo C ++ 1.01: # define RAND_MAX 0x7FFFU
  • El no tan antiguo C ++ Builder 5.5: # define RAND_MAX 0x7FFFU
  • El aún abierto Open Watcom C / C ++ 1.9: # define RAND_MAX 32767U
  • DJGPP (gcc 3.3.4 para DOS): # define RAND_MAX 2147483647
  • MinGW (gcc 4.6.2 para Windows): # define RAND_MAX 0x7FFF
  • MS Visual Studio 2010 ( enlace ): RAND_MAX se define como el valor 0x7fff
  • Comstackdor Tiny C 0.9.25: # define RAND_MAX 0x7FFF
  • lcc-win32 3.8: #define RAND_MAX 0x7fff
  • Pelles C 6.50: #define RAND_MAX 0x3fffffff O #define RAND_MAX 0x7fff
  • Digital Mars C / C ++ 8.52: # define RAND_MAX 32767

Esto hace que el código aparentemente inocuo como el que se muestra a continuación se vuelva no portátil y explote debido a la promoción firmada a no firmada:

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

rand() devuelve un signed int en el rango [0, RAND_MAX ].

Si RAND_MAX se define como un unsigned int , el valor de rand() se promueve a unsigned int .

Y si ese es el caso, la diferencia (rand() - RAND_MAX / 2) convierte en una diferencia sin signo de enteros sin signo con el valor en los rangos [0, RAND_MAXRAND_MAX / 2] & [ UINT_MAX + 1- RAND_MAX / 2, UINT_MAX -1] en lugar de ser una diferencia con signo de enteros con signo con el valor en el rango [- RAND_MAX / 2, RAND_MAXRAND_MAX / 2].

De todos modos, parece que RAND_MAX debería estar firmado y la mayoría de los (?) Comstackdores lo definen como tal, pero ¿hay alguna fuente autorizada que diga que debería estar firmada? Estándar más antiguo? K&R? Otra especificación de UNIX?

La respuesta es: ese código está haciendo una suposición injustificada, por lo que necesita ser corregido. Lo que debería haber hecho ese código es convertir RAND_MAX a (int) en uso.

Si bien tendría más sentido que los comstackdores definieran sus macros RAND_MAX como firmadas, el estándar evita cuidadosamente que lo hagan. Eso lo convierte en un error de portabilidad para que cualquier código asum ciegamente que está firmado.

Sí, esto parece un defecto en la norma.

Primero, hoy en día probablemente nadie definiría rand() para devolver un int . La intención es claramente devolver un número positivo y no hay retorno de error que pueda usar los rendimientos negativos. Dicha función, si se introdujera hoy, se diseñaría con un tipo entero sin signo como tipo de retorno. Mi conjetura es que es anterior al C89 y la introducción del concepto de enteros sin signo al lenguaje.

Entonces, claramente, uno tiene que esperar que una definición del valor máximo que devuelve una función tenga el mismo tipo que la de la función. En otros lugares, las macros están bien definidas como expansión a expresiones con un cierto tipo, por lo que esto también habría sido posible aquí.

En cuanto a su problema, creo que lo más fácil de tener es algo portátil, es escalar el valor primero a un doble en [0, 1) haciendo algo como rand()/(RAND_MAX+1.0) y derivar todos sus cálculos a partir de ahí. En cualquier caso, debe usar rand() solo como último recurso si su plataforma no tiene un mejor generador pseudoaleatorio disponible. Por ejemplo, en los sistemas POSIX, la familia rand48 es un reemplazo conveniente con propiedades bien descritas.