Du's Time

溢出无处不在

Word count: 394 / Reading time: 2 min
2016/02/29 Share

#

最近需要做个随机数,一般随机数不都是用时间戳作种子然后生成嘛,于是迅速写下以下代码

1
2
3
4
5
6
7
//取当前时间
NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970];
//将时间戳的精度取到毫秒级,减少种子雷同的概率
NSTimeInterval timeStamp = (long)(currentTime * 1000);
//srand要求time
srand((unsigned int)timeStamp);
NSInteger randomNumber = rand();

代码愉快地上线之后,居然有1/5的概率会生成重复的随机数。重新review代码之后,发现太久没有用unsigned int等基本类型,完全无视了各种类型转换赋值之间精度丢失和溢出的问题。

我们先来复习一下32位下各种基本类型的取值范围

类型 最小值 最大值 字节
short int -32768 32767 2
unsigned short int 0 65535 2
int -2147483648 2147483647 4
unsigned int 0 4294967295 4
long -2147483648 2147483647 4
unsigned long 0 4294967295 4
long long -9 223 372 036 854 775 808 9 223 372 036 854 775 807 8
unsigned long long 0 18 446 744 073 709 551 615 8
float 1.17549e-038 3.40282e+038 4
double 2.22507e-308 1.79769e+308 8

然后再看代码:

1
NSTimeInterval timeStamp = (long)(currentTime * 1000);

NSTimeIntervaldouble类型,这里将8字节的double类型强制转换成了4字节long[[NSDate date] timeIntervalSince1970]获取到的整数部分有11位,再乘以1000之后有14位,妥妥的超过了long的取值范围,于是这里愉快地发生了溢出。老师说过,一个正数溢出之后就变成了最小的负数,所以在这一步,我们本来正正常常的时间戳的种子刷的一下都变成了负数 -2147483648,因此在32位下,无论当前的时间戳是多少,都会生成同样的随机数。

CATALOG