REdis zset和double

时间:2023-03-09 15:49:13
REdis zset和double

平台:x86_64

结论:Zset的最大分数不要超过18014398509481982(17位数字,54位二进制),否则不会得到期望的值。

REdis:5.0.4

Zset采用double存储分数值(score),而incrbyfloat和hincrbyfloat采用的是long double存储数值。

double本身是可以存储比18014398509481982(17位数字,54位二进制)大的值,比如18014398509481983(也是17位数字,54位二进制)。但REdis在返回结果时,调用strtod将值转成double类型。

取值源代码:

double zzlGetScore(unsigned char *sptr) { // t_zset.c:722

unsigned char *vstr;

unsigned int vlen;

long long vlong;

char buf[128];

double score;

serverAssert(sptr != NULL);

serverAssert(ziplistGet(sptr,&vstr,&vlen,&vlong));

if (vstr) {

memcpy(buf,vstr,vlen);

buf[vlen] = '\0';

// 调用库函数strtod,

// 如果值小于18014398509481983,能得到预期的值,

// 否则总是返回18014398509481984(2的54次方)。

score = strtod(buf,NULL); // 字符串转double

} else {

score = vlong;

}

return score;

}

以字符串形式返回:

/* Add a double as a bulk reply */

void addReplyDouble(client *c, double d) { // networking.c:471

char dbuf[128], sbuf[128];

int dlen, slen;

if (isinf(d)) {

/* Libc in odd systems (Hi Solaris!) will format infinite in a

* different way, so better to handle it in an explicit way. */

addReplyBulkCString(c, d > 0 ? "inf" : "-inf");

} else {

dlen = snprintf(dbuf,sizeof(dbuf),"%.17g",d); // double转字符串

slen = snprintf(sbuf,sizeof(sbuf),"$%d\r\n%s\r\n",dlen,dbuf);

addReplyString(c,sbuf,slen);

}

}

x86_64上的测试:

127.0.0.1:6379> del k1

(integer) 1

127.0.0.1:6379> zadd k1 18014398509481982 m1

(integer) 1

127.0.0.1:6379> zrange k1 0 -1 WITHSCORES

1) "m1"

2) "18014398509481982" 预期的值

127.0.0.1:6379> del k1

(integer) 1

127.0.0.1:6379> zadd k1 18014398509481983 m1

(integer) 1

127.0.0.1:6379> zrange k1 0 -1 WITHSCORES

1) "m1"

2) "18014398509481984" 非预期的值(和strtod相关)

127.0.0.1:6379> del k1

(integer) 1

127.0.0.1:6379> zadd k1 18014398509481982 m1

(integer) 1

127.0.0.1:6379> zrange k1 0 -1 WITHSCORES

1) "m1"

2) "18014398509481982" 预期的值

127.0.0.1:6379> zincrby k1 1 m1

"18014398509481984" 非预期的值

127.0.0.1:6379> zrange k1 0 -1 WITHSCORES

1) "m1"

2) "18014398509481984" 非预期的值

附:C/C++浮点知识图谱

REdis zset和double