生成点云数据做仿真的时候遇到了这样一个问题,点云的坐标用随机数生成,语句如下:
cloud.points[i].x = 512 * rand () / (RAND_MAX + 1.0f);
开始没有仔细想,以为512就是个比例系数,生成的点云范围在512m内,但实测后发现,不管如何更改这个比例,实际生成的点云都在-1~1之间。研究一下随机数生成的函数,发现了奥秘。rand()函数的解释如下
int rand()
Return a random integer between 0 and RAND_MAX inclusive.返回0-RAND_MAX的任意值
我的初始理解是rand () / (RAND_MAX + 1.0f)
取一个[0,1)的值,然后乘上比例系数,这当然不对,因为运算顺序显然是从左向右的。也就是说,rand()的结果返回的整数要先和系数512相乘,再与范围作比。由于是512默认是int整型,rand()返回的值也是整形,而且RAND_MAX和INT_MAX一般相同,都是2147483647,所以这里先乘就极有可能越界,超出范围的数会被计算机理解为负值,再除以范围,得到的就是(-1,1)之间的值,这和上面的表现是一致的。
查资料的时候网上有很多类似转换的例子,写一段比较的方法如下:
for(int i = 0; i < 10 ; ++i)
{
//固定一个随机数
int r = rand();
//打印固定的随机数
std::cout << "rand = " << r << std::endl;
//不加系数的结果
std::cout << "rand/max = " << r/(RAND_MAX+1.0f) << std::endl;
//乘上一个整数10的结果
std::cout << "10 * r/(RAND_MAX+1.0f) = " << 10 * r/(RAND_MAX+1.0f) << std::endl;
// 我的错误理解,先除再缩放的结果
std::cout << "10 * (r/(RAND_MAX+1.0f)) = " << 10 * (r/(RAND_MAX+1.0f)) << std::endl;
//网上常用的生成随机数的方法,乘的系数是10.0,用double型直接扩充数值范围,得到的结果和我的错误理解类似,再用int强制类型转换,取个整数部分
std::cout << "((int)(10.0*r / (RAND_MAX + 1.0))) = " << ((int)(10.0*r / (RAND_MAX + 1.0))) << std::endl;
}
运行得到的部分结果如下,可以看到,只有10 * r/(RAND_MAX+1.0f)
这种情况会出现负值,原因已经在上面分析过。正常取负值的方式可以用2倍范围减一半的方式,例如随机(-10,10)可以用随机得到(0,1)然后取20倍,再整体减掉10。
# results
rand = 218746804
rand/max = 0.101862
10 * r/(RAND_MAX+1.0f) = -0.981381 //有负值
10 * (r/(RAND_MAX+1.0f)) = 1.01862 // 10倍值
((int)(10.0*r / (RAND_MAX + 1.0))) = 1 //10倍值的整数部分
rand = 231106831
rand/max = 0.107618
10 * r/(RAND_MAX+1.0f) = -0.923825 //有负值
10 * (r/(RAND_MAX+1.0f)) = 1.07618
((int)(10.0*r / (RAND_MAX + 1.0))) = 1
rand = 1109549898
rand/max = 0.516674
10 * r/(RAND_MAX+1.0f) = -0.833256 //有负值,当然也有正值,没列出来
10 * (r/(RAND_MAX+1.0f)) = 5.16674
((int)(10.0*r / (RAND_MAX + 1.0))) = 5
......