Skip to content

Commit 02b98f3

Browse files
committed
Optimize the speed of ldouble algorithm
1 parent fe2f200 commit 02b98f3

File tree

7 files changed

+514
-176
lines changed

7 files changed

+514
-176
lines changed

Makefile

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ testedbin = ljson
1212
testednum = jnum_test
1313

1414
INSTALL_HEADERS = json.h jnum.h
15+
FMUL ?= 1
1516
DTOA ?= 0
16-
RBIT ?= 11
1717
TCMP ?= 2
1818

1919
libsrcs := json.c jnum.c
@@ -24,8 +24,8 @@ ifeq ($(DTOA),3)
2424
libsrcs += dragonbox.c
2525
endif
2626

27+
CPFLAGS += -DUSING_FLOAT_MUL=$(FMUL)
2728
CPFLAGS += -DJSON_DTOA_ALGORITHM=$(DTOA) # 0:ldouble 1:sprintf 2:grisu2 3:dragonbox
28-
CPFLAGS += -DLSHIFT_RESERVED_BIT=$(RBIT) # 2 <= RBIT <= 11
2929
CPFLAGS += -DAPPROX_TAIL_CMP_VAL=$(TCMP) # 0 <= TCMP <= 4
3030

3131
.PHONY: all clean install
@@ -40,7 +40,7 @@ $(eval $(call add-liba-build,$(staticlib),$(libsrcs)))
4040
$(eval $(call add-libso-build,$(sharedlib),$(libsrcs)))
4141
$(eval $(call add-bin-build,$(testedbin),json_test.c,-L $(OBJ_PREFIX) $(call set_links,ljson),,$(OBJ_PREFIX)/$(staticlib)))
4242

43-
numsrcs := json.c jnum.c grisu2.c dragonbox.c jnum_test.c
43+
numsrcs := jnum.c grisu2.c dragonbox.c jnum_test.c
4444
$(eval $(call add-bin-build,$(testednum),$(numsrcs)))
4545

4646
all: $(BIN_TARGETS) $(LIB_TARGETS)

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ By default, LJSON uses the personally developed ldouble algorithm to print doubl
2020
* Compile directly
2121

2222
```sh
23-
gcc -o ljson json.c json_test.c -O2 -ffunction-sections -fdata-sections -W -Wall
23+
gcc -o ljson json.c -jnum.c json_test.c -O2 -ffunction-sections -fdata-sections -W -Wall
2424
```
2525

2626
* Compile with [IMAKE](https://github.com/lengjingzju/cbuild-ng)

README_zh-cn.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ LJSON 默认使用个人开发的 ldouble 算法打印浮点数,和标准库
2020
* 直接编译
2121

2222
```sh
23-
gcc -o ljson json.c json_test.c -O2 -ffunction-sections -fdata-sections -W -Wall
23+
gcc -o ljson json.c jnum.c json_test.c -O2 -ffunction-sections -fdata-sections -W -Wall
2424
```
2525

2626
* [IMAKE](https://github.com/lengjingzju/cbuild-ng) 编译

dragonbox.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1201,7 +1201,7 @@ int dragonbox_dtoa(double num, char *buffer)
12011201
break;
12021202

12031203
case 0:
1204-
if (!significand) {
1204+
if (significand) {
12051205
/* no-normalized double */
12061206
v.f = significand; /* Non-normalized double doesn't have a extra integer bit for Mantissa */
12071207
v.e = 1 - DP_EXPONENT_OFFSET - DP_SIGNIFICAND_SIZE; /* Fixed Exponent: -1022, divisor of Mantissa: pow(2,52) */

jnum.c

+397-86
Large diffs are not rendered by default.

jnum_test.c

+88-53
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
#include <stdio.h>
22
#include <stdlib.h>
33
#include <string.h>
4-
#include <unistd.h>
54
#include <time.h>
5+
#include "jnum.h"
66

7-
extern int jnum_dtoa(double num, char *buffer);
87
extern int grisu2_dtoa(double num, char *buffer);
98
extern int dragonbox_dtoa(double num, char *buffer);
109

@@ -17,71 +16,107 @@ static unsigned int _system_ms_get(void)
1716

1817
int main(int argc, char *argv[])
1918
{
19+
char buf[64] = {0};
20+
2021
if (argc != 2 && argc != 3) {
2122
printf("Usage: %s <num>\n", argv[0]);
2223
printf("Usage: %s <num> <cnt>\n", argv[0]);
24+
printf("Usage: %s <i/l/h/L/d> <num>\n", argv[0]);
2325
return -1;
2426
}
2527

26-
char *tmp = NULL;
27-
double d = strtod(argv[1], &tmp);
28-
char buf[64] = {0};
29-
30-
if (argc == 2) {
31-
#if 1
32-
printf("original : %s\nprintf : %0.15g\n", argv[1], d);
33-
jnum_dtoa(d, buf);
34-
printf("ldouble : %s\n", buf);
35-
grisu2_dtoa(d, buf);
36-
printf("grisu2 : %s\n", buf);
37-
dragonbox_dtoa(d, buf);
38-
printf("dragonbox : %s\n", buf);
39-
#else
40-
char buf2[64] = {0};
41-
42-
sprintf(buf2, "%0.15g", d);
43-
jnum_dtoa(d, buf);
44-
if (strcmp(buf, buf2)) {
45-
printf("original : %s\nprintf : %0.15g\n", argv[1], d);
28+
switch (*argv[1]) {
29+
case 'i':
30+
{
31+
int32_t d = jnum_atoi(argv[2]);
32+
jnum_itoa(d, buf);
33+
printf("original : %s\nprintf : %d\njnum : %s\n", argv[2], d, buf);
34+
break;
35+
}
36+
case 'l':
37+
{
38+
int64_t d = jnum_atol(argv[2]);
39+
jnum_ltoa(d, buf);
40+
printf("original : %s\nprintf : %ld\njnum : %s\n", argv[2], d, buf);
41+
break;
42+
}
43+
case 'h':
44+
{
45+
uint32_t d = jnum_atoh(argv[2]);
46+
jnum_htoa(d, buf);
47+
printf("original : %s\nprintf : 0x%x\njnum : %s\n", argv[2], d, buf);
48+
break;
49+
}
50+
case 'L':
51+
{
52+
uint64_t d = jnum_atolh(argv[2]);
53+
jnum_lhtoa(d, buf);
54+
printf("original : %s\nprintf : 0x%lx\njnum : %s\n", argv[2], d, buf);
55+
break;
56+
}
57+
case 'd':
58+
{
59+
double d = jnum_atod(argv[2]);
60+
printf("original : %s\nprintf : %0.15g\n", argv[2], d);
61+
jnum_dtoa(d, buf);
4662
printf("ldouble : %s\n", buf);
63+
grisu2_dtoa(d, buf);
64+
printf("grisu2 : %s\n", buf);
65+
dragonbox_dtoa(d, buf);
66+
printf("dragonbox : %s\n", buf);
67+
break;
4768
}
48-
#endif
49-
} else {
50-
int i;
51-
int cnt = atoi(argv[2]);
52-
unsigned int ms, ms1, ms2, ms3;
5369

54-
ms1 = _system_ms_get();
55-
for (i = 0; i < cnt; ++i) {
56-
sprintf(buf, "%0.15g", d);
57-
}
58-
ms2 = _system_ms_get();
59-
ms = ms2 - ms1;
60-
printf("original : %s\nprintf : %0.15g\t%ums\n", argv[1], d, ms);
70+
default:
71+
if (argc == 2) {
72+
double d = jnum_atod(argv[1]);
73+
char tmp[64] = {0};
6174

62-
ms1 = _system_ms_get();
63-
for (i = 0; i < cnt; ++i) {
75+
sprintf(tmp, "%0.15g", d);
6476
jnum_dtoa(d, buf);
65-
}
66-
ms2 = _system_ms_get();
67-
ms3 = ms2 - ms1;
68-
printf("ldouble : %s\t%ums\t%.0lf%%\n", buf, ms3, ms3 ? 100.0 * ms / ms3 : 0);
77+
if (strcmp(buf, tmp)) {
78+
printf("original : %s\nprintf : %s\n", argv[1], tmp);
79+
printf("ldouble : %s\n", buf);
80+
}
81+
} else {
82+
int i;
83+
double d = jnum_atod(argv[1]);
84+
int cnt = atoi(argv[2]);
85+
unsigned int ms, ms1, ms2, ms3;
6986

70-
ms1 = _system_ms_get();
71-
for (i = 0; i < cnt; ++i) {
72-
grisu2_dtoa(d, buf);
73-
}
74-
ms2 = _system_ms_get();
75-
ms3 = ms2 - ms1;
76-
printf("grisu2 : %s\t%ums\t%.0lf%%\n", buf, ms3, ms3 ? 100.0 * ms / ms3 : 0);
87+
ms1 = _system_ms_get();
88+
for (i = 0; i < cnt; ++i) {
89+
sprintf(buf, "%0.15g", d);
90+
}
91+
ms2 = _system_ms_get();
92+
ms = ms2 - ms1;
93+
printf("original : %s\nprintf : %0.15g\t%ums\n", argv[1], d, ms);
7794

78-
ms1 = _system_ms_get();
79-
for (i = 0; i < cnt; ++i) {
80-
dragonbox_dtoa(d, buf);
95+
ms1 = _system_ms_get();
96+
for (i = 0; i < cnt; ++i) {
97+
jnum_dtoa(d, buf);
98+
}
99+
ms2 = _system_ms_get();
100+
ms3 = ms2 - ms1;
101+
printf("ldouble : %s\t%ums\t%.0lf%%\n", buf, ms3, ms3 ? 100.0 * ms / ms3 : 0);
102+
103+
ms1 = _system_ms_get();
104+
for (i = 0; i < cnt; ++i) {
105+
grisu2_dtoa(d, buf);
106+
}
107+
ms2 = _system_ms_get();
108+
ms3 = ms2 - ms1;
109+
printf("grisu2 : %s\t%ums\t%.0lf%%\n", buf, ms3, ms3 ? 100.0 * ms / ms3 : 0);
110+
111+
ms1 = _system_ms_get();
112+
for (i = 0; i < cnt; ++i) {
113+
dragonbox_dtoa(d, buf);
114+
}
115+
ms2 = _system_ms_get();
116+
ms3 = ms2 - ms1;
117+
printf("dragonbox : %s\t%ums\t%.0lf%%\n", buf, ms3, ms3 ? 100.0 * ms / ms3 : 0);
81118
}
82-
ms2 = _system_ms_get();
83-
ms3 = ms2 - ms1;
84-
printf("dragonbox : %s\t%ums\t%.0lf%%\n", buf, ms3, ms3 ? 100.0 * ms / ms3 : 0);
119+
break;
85120
}
86121

87122
return 0;

json.c

+23-31
Original file line numberDiff line numberDiff line change
@@ -2060,10 +2060,10 @@ static json_type_t _parse_hex(const char **sstr, json_number_t *value)
20602060
c -= '0';
20612061
break;
20622062
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
2063-
c -= 'a';
2063+
c -= 'a' - 10;
20642064
break;
20652065
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
2066-
c -= 'A';
2066+
c -= 'A' - 10;
20672067
break;
20682068
default:
20692069
goto end;
@@ -2107,26 +2107,22 @@ static json_type_t _parse_num(const char **sstr, json_number_t *value)
21072107
while (*s == '0')
21082108
++s;
21092109

2110-
while (1) {
2111-
switch (*s) {
2112-
case '0': case '1': case '2': case '3': case '4':
2113-
case '5': case '6': case '7': case '8': case '9':
2114-
break;
2115-
case 'e': case 'E':
2116-
goto end;
2117-
case '.':
2118-
goto next2;
2119-
default:
2120-
goto next1;
2121-
}
2110+
while (*s >= '0' && *s <= '9') {
21222111
m = (m << 3) + (m << 1) + (*s++ - '0');
21232112
++k;
21242113
}
2125-
2126-
next1:
21272114
if (unlikely(k >= 20))
21282115
goto end;
21292116

2117+
switch (*s) {
2118+
case 'e': case 'E':
2119+
goto end;
2120+
case '.':
2121+
goto next;
2122+
default:
2123+
break;
2124+
}
2125+
21302126
if (m <= 2147483647U /*INT_MAX*/) {
21312127
type = JSON_INT;
21322128
value->vint = sign == 1 ? (int32_t)m : -((int32_t)m);
@@ -2138,34 +2134,30 @@ static json_type_t _parse_num(const char **sstr, json_number_t *value)
21382134
type = JSON_LINT;
21392135
value->vlint = -m;
21402136
} else {
2141-
goto end;
2137+
type = JSON_DOUBLE;
2138+
value->vdbl = sign == 1 ? (double)m : -((double)m);
21422139
}
21432140
}
21442141
*sstr = s;
21452142
return type;
21462143

2147-
next2:
2148-
if (unlikely(k >= 20))
2149-
goto end;
2150-
2144+
next:
21512145
++s;
21522146
k = 0;
2153-
while (1) {
2154-
switch (*s) {
2155-
case '0': case '1': case '2': case '3': case '4':
2156-
case '5': case '6': case '7': case '8': case '9':
2157-
break;
2158-
default:
2159-
goto next3;
2160-
}
2147+
while (*s >= '0' && *s <= '9') {
21612148
n = (n << 3) + (n << 1) + (*s++ - '0');
21622149
++k;
21632150
}
2164-
2165-
next3:
21662151
if (unlikely(k >= 20))
21672152
goto end;
21682153

2154+
switch (*s) {
2155+
case 'e': case 'E':
2156+
goto end;
2157+
default:
2158+
break;
2159+
}
2160+
21692161
d = m + n * div10_lut[k];
21702162
type = JSON_DOUBLE;
21712163
value->vdbl = sign == 1 ? d : -d;

0 commit comments

Comments
 (0)