diff --git a/src/object.c b/src/object.c index 5d72abb8..43c371ea 100644 --- a/src/object.c +++ b/src/object.c @@ -135,37 +135,7 @@ robj *createStringObjectFromLongLong(long long value) { * The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */ robj *createStringObjectFromLongDouble(long double value, int humanfriendly) { char buf[256]; - int len; - - if (isinf(value)) { - /* Libc in odd systems (Hi Solaris!) will format infinite in a - * different way, so better to handle it in an explicit way. */ - if (value > 0) { - memcpy(buf,"inf",3); - len = 3; - } else { - memcpy(buf,"-inf",4); - len = 4; - } - } else if (humanfriendly) { - /* We use 17 digits precision since with 128 bit floats that precision - * after rounding is able to represent most small decimal numbers in a - * way that is "non surprising" for the user (that is, most small - * decimal numbers will be represented in a way that when converted - * back into a string are exactly the same as what the user typed.) */ - len = snprintf(buf,sizeof(buf),"%.17Lf", value); - /* Now remove trailing zeroes after the '.' */ - if (strchr(buf,'.') != NULL) { - char *p = buf+len-1; - while(*p == '0') { - p--; - len--; - } - if (*p == '.') len--; - } - } else { - len = snprintf(buf,sizeof(buf),"%.17Lg", value); - } + int len = ld2string(buf,sizeof(buf),value,humanfriendly); return createStringObject(buf,len); } diff --git a/src/t_hash.c b/src/t_hash.c index 9019112e..eaf4d245 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -545,10 +545,12 @@ void hincrbyCommand(client *c) { server.dirty++; } +/* XXX From here. */ + void hincrbyfloatCommand(client *c) { double value, incr; long long ll; - robj *o, *doubleobj; + robj *o; sds new; unsigned char *vstr; unsigned int *lven; @@ -569,10 +571,12 @@ void hincrbyfloatCommand(client *c) { } value += incr; - doubleobj = createStringObjectFromLongDouble(value,1); - decrRefCount(doubleobj); + + char buf[256]; + int len = ld2string(buf,sizeof(buf),value,humanfriendly); + new = sdsnewlen(buf,len); hashTypeSet(o,c->argv[2],new); - addReplyBulk(c,new); + addReplyBulkSds(c,new); signalModifiedKey(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_HASH,"hincrbyfloat",c->argv[1],c->db->id); server.dirty++; @@ -580,11 +584,13 @@ void hincrbyfloatCommand(client *c) { /* Always replicate HINCRBYFLOAT as an HSET command with the final value * in order to make sure that differences in float pricision or formatting * will not create differences in replicas or after an AOF restart. */ + robj *aux, *newobj; aux = createStringObject("HSET",4); + newobj = createRawStringObject(buf,len); rewriteClientCommandArgument(c,0,aux); decrRefCount(aux); rewriteClientCommandArgument(c,3,new); - decrRefCount(new); + decrRefCount(newobj); } static void addHashFieldToReply(client *c, robj *o, robj *field) { diff --git a/src/util.c b/src/util.c index 4a000bc6..80e316c9 100644 --- a/src/util.c +++ b/src/util.c @@ -444,7 +444,10 @@ int string2d(const char *s, size_t slen, double *dp) { } /* Convert a double to a string representation. Returns the number of bytes - * required. The representation should always be parsable by strtod(3). */ + * required. The representation should always be parsable by strtod(3). + * This function does not support human-friendly formatting like ld2string + * does. It is intented mainly to be used inside t_zset.c when writing scores + * into a ziplist representing a sorted set. */ int d2string(char *buf, size_t len, double value) { if (isnan(value)) { len = snprintf(buf,len,"nan"); @@ -482,6 +485,53 @@ int d2string(char *buf, size_t len, double value) { return len; } +/* Convert a long double into a string. If humanfriendly is non-zero + * it does not use exponential format and trims trailing zeroes at the end, + * however this results in loss of precision. Otherwise exp format is used + * and the output of snprintf() is not modified. + * + * The function returns the length of the string or zero if there was not + * enough buffer room to store it. */ +int ld2string(char *buf, size_t len, long double value, int humanfriendly) { + char buf[256]; + size_t l; + + if (isinf(value)) { + /* Libc in odd systems (Hi Solaris!) will format infinite in a + * different way, so better to handle it in an explicit way. */ + if (len < 5) return 0; /* No room. 5 is "-inf\0" */ + if (value > 0) { + memcpy(buf,"inf",3); + l = 3; + } else { + memcpy(buf,"-inf",4); + l = 4; + } + } else if (humanfriendly) { + /* We use 17 digits precision since with 128 bit floats that precision + * after rounding is able to represent most small decimal numbers in a + * way that is "non surprising" for the user (that is, most small + * decimal numbers will be represented in a way that when converted + * back into a string are exactly the same as what the user typed.) */ + l = snprintf(buf,len,"%.17Lf", value); + if (l+1 > len) return 0; /* No room. */ + /* Now remove trailing zeroes after the '.' */ + if (strchr(buf,'.') != NULL) { + char *p = buf+len-1; + while(*p == '0') { + p--; + len--; + } + if (*p == '.') len--; + } + } else { + l = snprintf(buf,len,"%.17Lg", value); + if (l+1 > len) return 0; /* No room. */ + } + buf[l] = '\0'; + return l; +} + /* Generate the Redis "Run ID", a SHA1-sized random number that identifies a * given execution of Redis, so that if you are talking with an instance * having run_id == A, and you reconnect and it has run_id == B, you can be diff --git a/src/util.h b/src/util.h index 8ec4228f..d28049ea 100644 --- a/src/util.h +++ b/src/util.h @@ -43,6 +43,7 @@ int string2ll(const char *s, size_t slen, long long *value); int string2l(const char *s, size_t slen, long *value); int string2d(const char *s, size_t slen, double *dp); int d2string(char *buf, size_t len, double value); +int ld2string(char *buf, size_t len, long double value, int humanfriendly); sds getAbsolutePath(char *filename); int pathIsBaseName(char *path);