Skip to content

Commit

Permalink
Normalize NAN to a single nan type, like we do with inf (redis#11597)
Browse files Browse the repository at this point in the history
From https://en.wikipedia.org/wiki/NaN#Display, it says
that apart from nan and -nan, we can also get NAN and even
nan(char-sequence) from libc.

In redis#11482, our conclusion was that we wanna normalize it in
Redis to a single nan type, like we already normalized inf.

For this, we also reverted the assert_match part of the test
added in redis#11506, using assert_equal to validate the changes.
  • Loading branch information
enjoy-binbin authored and madolson committed Apr 19, 2023
1 parent b9bb10d commit 2afdd25
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 7 deletions.
11 changes: 10 additions & 1 deletion src/networking.c
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,15 @@ void addReplyDouble(client *c, double d) {
addReplyProto(c, d > 0 ? ",inf\r\n" : ",-inf\r\n",
d > 0 ? 6 : 7);
}
} else if (isnan(d)) {
/* Libc in some systems will format nan in a different way,
* like nan, -nan, NAN, nan(char-sequence).
* So we normalize it and create a single nan form in an explicit way. */
if (c->resp == 2) {
addReplyBulkCString(c, "nan");
} else {
addReplyProto(c, ",nan\r\n", 6);
}
} else {
char dbuf[MAX_LONG_DOUBLE_CHARS+32];
int dlen = 0;
Expand All @@ -864,7 +873,7 @@ void addReplyDouble(client *c, double d) {
dbuf[start] = '$';

/* Convert `dlen` to string, putting it's digits after '$' and before the
* formatted double string. */
* formatted double string. */
for(int i = digits, val = dlen; val && i > 0 ; --i, val /= 10) {
dbuf[start + i] = "0123456789"[val % 10];
}
Expand Down
19 changes: 19 additions & 0 deletions src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,13 @@ int ld2string(char *buf, size_t len, long double value, ld2string_mode mode) {
memcpy(buf,"-inf",4);
l = 4;
}
} else if (isnan(value)) {
/* Libc in some systems will format nan in a different way,
* like nan, -nan, NAN, nan(char-sequence).
* So we normalize it and create a single nan form in an explicit way. */
if (len < 4) goto err; /* No room. 4 is "nan\0" */
memcpy(buf, "nan", 3);
l = 3;
} else {
switch (mode) {
case LD_STR_AUTO:
Expand Down Expand Up @@ -1253,6 +1260,17 @@ static void test_ll2string(void) {
assert(!strcmp(buf, "9223372036854775807"));
}

static void test_ld2string(void) {
char buf[32];
long double v;
int sz;

v = 0.0 / 0.0;
sz = ld2string(buf, sizeof(buf), v, LD_STR_AUTO);
assert(sz == 3);
assert(!strcmp(buf, "nan"));
}

static void test_fixedpoint_d2string(void) {
char buf[32];
double v;
Expand Down Expand Up @@ -1309,6 +1327,7 @@ int utilTest(int argc, char **argv, int flags) {
test_string2ll();
test_string2l();
test_ll2string();
test_ld2string();
test_fixedpoint_d2string();
return 0;
}
Expand Down
10 changes: 4 additions & 6 deletions tests/unit/moduleapi/reply.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,14 @@ start_server {tags {"modules"}} {
}

test "RESP$proto: RM_ReplyWithDouble: NaN" {
# On some platforms one of these can be -nan but we don't care since they are
# synonym, so here we match ignoring the sign
if {$proto == 2} {
assert_match "*nan" [r rw.double 0 0]
assert_match "*nan" [r rw.double]
assert_equal "nan" [r rw.double 0 0]
assert_equal "nan" [r rw.double]
} else {
# TCL won't convert nan into a double, use readraw to verify the protocol
r readraw 1
assert_match ",*nan" [r rw.double 0 0]
assert_match ",*nan" [r rw.double]
assert_equal ",nan" [r rw.double 0 0]
assert_equal ",nan" [r rw.double]
r readraw 0
}
}
Expand Down

0 comments on commit 2afdd25

Please sign in to comment.