forked from subsurface/subsurface
-
Notifications
You must be signed in to change notification settings - Fork 1
/
strtod.c
128 lines (116 loc) · 2.58 KB
/
strtod.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/*
* Sane helper for 'strtod()'.
*
* Sad that we even need this, but the C library version has
* insane locale behavior, and while the Qt "doDouble()" routines
* are better in that regard, they don't have an end pointer
* (having replaced it with the completely idiotic "ok" boolean
* pointer instead).
*
* I wonder what drugs people are on sometimes.
*
* Right now we support the following flags to limit the
* parsing some ways:
*
* STRTOD_NO_SIGN - don't accept signs
* STRTOD_NO_DOT - no decimal dots, I'm European
* STRTOD_NO_COMMA - no comma, please, I'm C locale
* STRTOD_NO_EXPONENT - no exponent parsing, I'm human
*
* The "negative" flags are so that the common case can just
* use a flag value of 0, and only if you have some special
* requirements do you need to state those with explicit flags.
*
* So if you want the C locale kind of parsing, you'd use the
* STRTOD_NO_COMMA flag to disallow a decimal comma. But if you
* want a more relaxed "Hey, Europeans are people too, even if
* they have locales with commas", just pass in a zero flag.
*/
#include <ctype.h>
#include "dive.h"
double strtod_flags(const char *str, const char **ptr, unsigned int flags)
{
char c;
const char *p = str, *ep;
double val = 0.0;
double decimal = 1.0;
int sign = 0, esign = 0;
int numbers = 0, dot = 0;
/* skip spaces */
while (isspace(c = *p++))
/* */;
/* optional sign */
if (!(flags & STRTOD_NO_SIGN)) {
switch (c) {
case '-':
sign = 1;
/* fallthrough */
case '+':
c = *p++;
}
}
/* Mantissa */
for (;; c = *p++) {
if ((c == '.' && !(flags & STRTOD_NO_DOT)) ||
(c == ',' && !(flags & STRTOD_NO_COMMA))) {
if (dot)
goto done;
dot = 1;
continue;
}
if (c >= '0' && c <= '9') {
numbers++;
val = (val * 10) + (c - '0');
if (dot)
decimal *= 10;
continue;
}
if (c != 'e' && c != 'E')
goto done;
if (flags & STRTOD_NO_EXPONENT)
goto done;
break;
}
if (!numbers)
goto done;
/* Exponent */
ep = p;
c = *ep++;
switch (c) {
case '-':
esign = 1;
/* fallthrough */
case '+':
c = *ep++;
}
if (c >= '0' && c <= '9') {
p = ep;
int exponent = c - '0';
for (;;) {
c = *p++;
if (c < '0' || c > '9')
break;
exponent *= 10;
exponent += c - '0';
}
/* We're not going to bother playing games */
if (exponent > 308)
exponent = 308;
while (exponent-- > 0) {
if (esign)
decimal *= 10;
else
decimal /= 10;
}
}
done:
if (!numbers)
goto no_conversion;
if (ptr)
*ptr = p - 1;
return (sign ? -val : val) / decimal;
no_conversion:
if (ptr)
*ptr = str;
return 0.0;
}