在做一个活动的需求时,需要往redis中有序的集合中存储一个小数,结果发现取出数据和存储时的数据不一致
zadd test_2017 1.1 tom (integer) 1 zrevrange test_2017 0 -1 withscores 1) "tom" 2) "1.1000000000000001" zadd test_2017 1.2 sam (integer) 1 zrevrange test_2017 0 -1 withscores 1) "sam" 2) "1.2" 3) "tom" 4) "1.1000000000000001"
是不是很奇怪, 存储tom的score 为1.1,结果为 1.1000000000000001,存储 sam的score为1.2,结果就是1.2
1.19999999999999995559 第16位是9,第17位是5, 四舍五入1.2
1.10000000000000008882 第16位是0, 第17位是8,四舍五入1.10000000000000001
但在php中
<?php echo 1.1;
确实显示1.1,是不是有点奇怪,后来在php.ini中找到到precision这个选项, 指
; The number of significant digits displayed in floating point numbers. ; http://php.net/precision precision = 14
因为在php中,小数都是以double形式存储的,那么1.10000000000000008882中第14位为0,第15位也为0,四舍五入,为1.1
解决方法:php在处理时使用bcmul函数,将小数*100,再存入redis,待取出来时,再除以100
看了下redis zadd的源码,发现 zadd key score name 这个命令,redis利用strtod这个函数 将score 转为double浮点数
/* This generic command implements both ZADD and ZINCRBY. */ void zaddGenericCommand(redisClient *c, int incr) { static char *nanerr = "resulting score is not a number (NaN)"; robj *key = c->argv[1]; robj *ele; robj *zobj; robj *curobj; double score = 0, *scores, curscore = 0.0; int j, elements = (c->argc-2)/2; int added = 0; if (c->argc % 2) { addReply(c,shared.syntaxerr); return; } /* Start parsing all the scores, we need to emit any syntax error * before executing additions to the sorted set, as the command should * either execute fully or nothing at all. */ scores = zmalloc(sizeof(double)*elements); for (j = 0; j < elements; j++) { if (getDoubleFromObjectOrReply(c,c->argv[2+j*2],&scores[j],NULL) != REDIS_OK) { zfree(scores); return; } } 。。。。 }
int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) { double value; if (getDoubleFromObject(o, &value) != REDIS_OK) { if (msg != NULL) { addReplyError(c,(char*)msg); } else { addReplyError(c,"value is not a valid float"); } return REDIS_ERR; } *target = value; return REDIS_OK; } int getDoubleFromObject(robj *o, double *target) { double value; char *eptr; if (o == NULL) { value = 0; } else { redisAssertWithInfo(NULL,o,o->type == REDIS_STRING); if (o->encoding == REDIS_ENCODING_RAW) { errno = 0; value = strtod(o->ptr, &eptr); if (isspace(((char*)o->ptr)[0]) || eptr[0] != '