c - 使用带有 rand() 的位移来允许更大的随机范围
问题描述
我正在审查一个为基数映射生成键的函数,并发现 rand() 的实现对我来说很新颖。
这是功能:
static int make_random(RadixMap *map)
{
size_t i = 0;
for (i = 0; i < map->max - 1; i++){
uint32_t key = (uint32_t) (rand() | (rand() << 16));<--This was interesting
check(RadixMap_add(map, key, i) == 0, "Failed to add key %u", key);
}
return i;
error:
return 0;
}
----- Type definitions --------
typedef union RMElement {
uint64_t raw;
struct {
uint32_t key;
uint32_t value;
} data;
} RMElement;
typedef struct RadixMap {
size_t max;
size_t end;
uint32_t counter;
RMElement *contents;
RMElement *temp;
} RadixMap;
来自 Zed Shaw 的 ex35 Learn C the Hard Way
我发现有趣的具体部分是
uint32_t key = (uint32_t) (rand() | (rand() << 16)); <-- This was interesting
这对我来说很有趣,因为它本来可以简单地做 ..
uint32_t key = rand();
由于 RAND_MAX (0x7FFFFFFF) 小于 uint32_t MAX (0xFFFFFFFF)
移位实现看起来具有以下优点。
- 允许更大的随机值范围,0xFFFFFFFF 与 0x7FFFFFFF
- 值(初始 0 除外)至少为 5 位十进制 (65537) (0x10001)
- 减少看到“0”的概率。
以及以下缺点
- 增加代码复杂度?
使用 rand() 的这种位移实现还有其他原因吗?
我一直试图在我的代码审查中找出使用这个实现的原因,并想确保我的想法是正确的。
解决方案
C 标准仅保证RAND_MAX
至少为 32767。此代码通过调用rand
两次并移位来确保它至少获得 30 位随机性来解释这一点。
但是,这并不能正确解释较大的RAND_MAX
情况。
该rand
函数返回一个int
已签名的。如果RAND_MAX
与 相同INT_MAX
,则rand() << 16
很可能会将“1”位移入符号位,从而触发未定义的行为。
实现这一点以处理这两种情况的正确方法是:
uint32_t key = rand() | ((uint32_t)rand() << 16));
由于左移一个无符号数,只要移位量小于类型的大小,就可以很好地定义。
或者更好:
uint32_t key = (((uint32_t)rand() & 0x7FFF) << 17) |
(((uint32_t)rand() & 0x7FFF) << 2) |
((uint32_t)rand() & 0x3);
获得完整的 32 位随机性。
推荐阅读
- .net - GitHub上DotNet repro中“@dotnet-bot test this please”背后的逻辑是什么?
- amazon-web-services - aws lambda 和 Rds 中的握手不活动超时错误
- reactjs - 启动我的反应本机应用程序时出现参考错误
- json - 在查询中解构深层嵌套的 json
- javascript - 选择类的 .child 元素应该在其他 .child 元素之上,每个 .child 都在 .parent 元素内,具有绝对位置
- swift - 恢复进入后台模式的外围 BLE 服务
- jquery - 在 URL (Rails) 中存储 AJAX 参数
- javascript - 为什么 XMLHttpRequest.setRequestHeader() 将 undefined 或 null 类型设置为字符串?
- css - 在 row.getRowProps() 中更改 Material-ui 表格样式
- c# - .Net 核心 3.1。需要嵌套分组按项目每日数量显示