-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodp_numtoa.cpp
159 lines (137 loc) · 3.65 KB
/
modp_numtoa.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// other interesting references on num to string convesion
// http://www.jb.man.ac.uk/~slowe/cpp/itoa.html
// and http://www.ddj.com/dept/cpp/184401596?pgno=6
//
// Version 19-Nov-2007
// Fixed round-to-even rules to match printf
// thanks to Johannes Otepka
#include <stdint.h>
#include <stdio.h>
#include <math.h>
#include "modp_numtoa.h"
namespace allyes {
namespace gmetric {
/**
* Powers of 10
* 10^0 to 10^9
*/
static const double pow10[] = {
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
};
static void strreverse(char* begin, char* end)
{
char aux;
while (end > begin) {
aux = *end, *end-- = *begin, *begin++ = aux;
}
}
void modp_itoa10(int32_t value, char* str)
{
char* wstr=str;
int sign;
// Take care of sign
if ((sign=value) < 0) value = -value;
// Conversion. Number is reversed.
do *wstr++ = (48 + (value % 10)); while( value /= 10);
if (sign < 0) *wstr++ = '-';
*wstr='\0';
// Reverse string
strreverse(str,wstr-1);
}
void modp_uitoa10(uint32_t value, char* str)
{
char* wstr=str;
// Conversion. Number is reversed.
do *wstr++ = 48 + (value % 10); while (value /= 10);
*wstr='\0';
// Reverse string
strreverse(str, wstr-1);
}
void modp_dtoa(double value, char* str, int prec)
{
/* if input is larger than thres_max, revert to exponential */
const double thres_max = (double)(0x7FFFFFFF);
double diff = 0.0;
char* wstr = str;
if (prec < 0) {
prec = 0;
} else if (prec > 9) {
/* precision of >= 10 can lead to overflow errors */
prec = 9;
}
/* we'll work in positive values and deal with the
negative sign issue later */
int neg = 0;
if (value < 0) {
neg = 1;
value = -value;
}
int whole = (int) value;
double tmp = (value - whole) * pow10[prec];
uint32_t frac = (uint32_t)(tmp);
diff = tmp - frac;
if (diff > 0.5) {
++frac;
/* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
if (frac >= pow10[prec]) {
frac = 0;
++whole;
}
} else if (diff == 0.5 && ((frac == 0) || (frac & 1))) {
/* if halfway, round up if odd, OR
if last digit is 0. That last part is strange */
++frac;
}
/* for very large numbers switch back to native sprintf for exponentials.
anyone want to write code to replace this? */
/*
normal printf behavior is to print EVERY whole number digit
which can be 100s of characters overflowing your buffers == bad
*/
if (value > thres_max) {
sprintf(str, "%e", neg ? -value : value);
return;
}
if (prec == 0) {
diff = value - whole;
if (diff > 0.5) {
/* greater than 0.5, round up, e.g. 1.6 -> 2 */
++whole;
} else if (diff == 0.5 && (whole & 1)) {
/* exactly 0.5 and ODD, then round up */
/* 1.5 -> 2, but 2.5 -> 2 */
++whole;
}
} else {
int count = prec;
// now do fractional part, as an unsigned number
do {
--count;
*wstr++ = 48 + (frac % 10);
} while (frac /= 10);
// add extra 0s
while (count-- > 0) *wstr++ = '0';
// add decimal
*wstr++ = '.';
}
// do whole part
// Take care of sign
// Conversion. Number is reversed.
do *wstr++ = 48 + (whole % 10); while (whole /= 10);
if (neg) {
*wstr++ = '-';
}
*wstr='\0';
strreverse(str, wstr-1);
}
} /* namespace gmetric */
} /* namespace allyes */