-
Notifications
You must be signed in to change notification settings - Fork 0
/
demo_strtod.cpp
130 lines (115 loc) · 3.38 KB
/
demo_strtod.cpp
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
129
130
#include <stdint.h>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
const int STRTOD_NBITS = 1500;
typedef
boost::multiprecision::number<boost::multiprecision::backends::cpp_bin_float<STRTOD_NBITS+4, boost::multiprecision::backends::digit_base_2> >
strtod_float_t;
typedef
boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<STRTOD_NBITS, STRTOD_NBITS, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void> >
strtod_uint_t;
// return # of digits
static int readDecimalDigits(const char* src, strtod_uint_t& val, int* pnUsed, int* sticky)
{
static const strtod_uint_t MAX_VAL = (std::numeric_limits<strtod_uint_t>::max()-9)/10;
int nd, nUsed = 0;
unsigned lsb = 0;
for (nd = 0; ; ++nd) {
unsigned c = src[nd];
unsigned dig = c - '0';
if (dig > 9)
break;
if (val <= MAX_VAL) {
val = val*10+dig;
++nUsed;
} else {
lsb |= dig;
}
}
*sticky |= (lsb != 0); // for tie breaker
*pnUsed = nUsed;
return nd;
}
const char *skipWhiteSpaces(const char *str)
{
// skip leading white spaces
while (*str && *str <= ' ') ++str;
return str;
}
double demo_strtod(const char* str, char** endptr)
{
const char* p = skipWhiteSpaces(str);
int neg = 0;
switch (p[0]) {
case '+': ++p; break;
case '-': ++p; neg = 1; break;
default: break;
}
strtod_uint_t mant = 0;
int nUsedInt, sticky=0;
int ni = readDecimalDigits(p, mant, &nUsedInt, &sticky);
int nf = 0;
int nUsedFract = 0;
if (p[ni]=='.')
nf = readDecimalDigits(&p[ni+1], mant, &nUsedFract, &sticky);
if (ni == 0 && nf == 0) {
// conversion failed
if (endptr)
*endptr = (char*)str;
return 0;
}
int exp = ni - nUsedInt - nUsedFract;
int nm = ni + nf + (p[ni]=='.');
const char* endptrval = &p[nm];
if (endptrval[0] == 'e' || endptrval[0] == 'E') {
p = endptrval + 1;
int nege = 0;
switch (p[0]) {
case '+': ++p; break;
case '-': ++p; nege = 1; break;
default: break;
}
strtod_uint_t absExpVal = 0;
int dummy1, dummy2;
int ne = readDecimalDigits(p, absExpVal, &dummy1, &dummy2);
if (ne != 0) {
endptrval = p + ne;
int64_t expVal = absExpVal < std::numeric_limits<int64_t>::max()/2 ?
static_cast<int64_t>(absExpVal) :
std::numeric_limits<int64_t>::max()/2;
if (nege)
expVal = -expVal;
expVal += exp;
const int MAX_EXP = 310;
const int MIN_EXP = -325-STRTOD_NBITS/3;
if (expVal < MIN_EXP)
exp = MIN_EXP;
else if (expVal > MAX_EXP)
exp = MAX_EXP;
else
exp = expVal;
}
}
double retval = 0;
if (mant != 0) {
if (exp != 0) {
strtod_float_t fVal = static_cast<strtod_float_t>(mant);
fVal *= pow(strtod_float_t(10.0), exp);
retval = static_cast<double>(fVal);
// the following complicated code is here because I don't know
// how to set LS bit of mantissa of cpp_bin_float
if (sticky != 0 && retval < DBL_MAX && retval < fVal) {
double nxt = nextafter(retval, DBL_MAX);
if (fVal-retval == nxt-fVal)
retval = nxt;
}
} else {
if (sticky != 0)
mant |= 1;
retval = static_cast<double>(mant);
}
}
if (endptr)
*endptr = (char*)endptrval;
return neg ? -retval : retval;
}