7 // The maximum number of significant (decimal!) figures in an IEEE
11 // The minimum size we'll allocate for a string. Since a string
12 // structure is already 12 bytes, and each naRef that points to it is
13 // 8, there isn't much point in being stingy.
16 static int tonum(unsigned char* s, int len, double* result);
17 static int fromnum(double val, unsigned char* s);
19 int naStr_len(naRef s)
21 if(!IS_STR(s)) return 0;
22 return s.ref.ptr.str->len;
25 char* naStr_data(naRef s)
27 if(!IS_STR(s)) return 0;
28 return s.ref.ptr.str->data;
31 static void setlen(struct naStr* s, int sz)
34 sz += 1; // Allow for an extra nul terminator
35 currSz = s->len+1 < MINLEN ? MINLEN : s->len+1;
36 waste = currSz - sz; // how much extra if we don't reallocate?
37 if(s->data == 0 || waste < 0 || waste > MINLEN) {
39 s->data = naAlloc(sz < MINLEN ? MINLEN : sz);
42 s->data[s->len] = 0; // nul terminate
45 naRef naStr_fromdata(naRef dst, char* data, int len)
47 if(!IS_STR(dst)) return naNil();
48 setlen(dst.ref.ptr.str, len);
49 memcpy(dst.ref.ptr.str->data, data, len);
53 naRef naStr_concat(naRef dest, naRef s1, naRef s2)
55 struct naStr* dst = dest.ref.ptr.str;
56 struct naStr* a = s1.ref.ptr.str;
57 struct naStr* b = s2.ref.ptr.str;
58 if(!(IS_STR(s1)&&IS_STR(s2)&&IS_STR(dest))) return naNil();
59 setlen(dst, a->len + b->len);
60 memcpy(dst->data, a->data, a->len);
61 memcpy(dst->data + a->len, b->data, b->len);
65 naRef naStr_substr(naRef dest, naRef str, int start, int len)
67 struct naStr* dst = dest.ref.ptr.str;
68 struct naStr* s = str.ref.ptr.str;
69 if(!(IS_STR(dest)&&IS_STR(str))) return naNil();
70 if(start + len > s->len) { dst->len = 0; dst->data = 0; return naNil(); }
72 memcpy(dst->data, s->data + start, len);
76 int naStr_equal(naRef s1, naRef s2)
78 struct naStr* a = s1.ref.ptr.str;
79 struct naStr* b = s2.ref.ptr.str;
80 if(a->data == b->data) return 1;
81 if(a->len != b->len) return 0;
82 if(memcmp(a->data, b->data, a->len) == 0) return 1;
86 naRef naStr_fromnum(naRef dest, double num)
88 struct naStr* dst = dest.ref.ptr.str;
89 unsigned char buf[DIGITS+8];
90 setlen(dst, fromnum(num, buf));
91 memcpy(dst->data, buf, dst->len);
95 int naStr_parsenum(char* str, int len, double* result)
97 return tonum(str, len, result);
100 int naStr_tonum(naRef str, double* out)
102 return tonum(str.ref.ptr.str->data, str.ref.ptr.str->len, out);
105 int naStr_numeric(naRef str)
108 return tonum(str.ref.ptr.str->data, str.ref.ptr.str->len, &dummy);
111 void naStr_gcclean(struct naStr* str)
113 if(str->len > MINLEN) {
120 ////////////////////////////////////////////////////////////////////////
121 // Below is a custom double<->string conversion library. Why not
122 // simply use sprintf and atof? Because they aren't acceptably
123 // platform independant, sadly. I've seen some very strange results.
124 // This works the same way everywhere, although it is tied to an
125 // assumption of standard IEEE 64 bit floating point doubles.
127 // In practice, these routines work quite well. Testing conversions
128 // of random doubles to strings and back, this routine is beaten by
129 // glibc on roundoff error 23% of the time, beats glibc in 10% of
130 // cases, and ties (usually with an error of exactly zero) the
132 ////////////////////////////////////////////////////////////////////////
134 // Reads an unsigned decimal out of the scalar starting at i, stores
135 // it in v, and returns the next index to start at. Zero-length
136 // decimal numbers are allowed, and are returned as zero.
137 static int readdec(unsigned char* s, int len, int i, double* v)
140 if(i >= len) return len;
141 while(i < len && s[i] >= '0' && s[i] <= '9') {
142 *v= (*v) * 10 + (s[i] - '0');
148 // Reads a signed integer out of the string starting at i, stores it
149 // in v, and returns the next index to start at. Zero-length
150 // decimal numbers (and length-1 strings like '+') are allowed, and
151 // are returned as zero.
152 static int readsigned(unsigned char* s, int len, int i, double* v)
155 if(i >= len) { *v = 0; return len; }
156 if(s[i] == '+') { i++; }
157 else if(s[i] == '-') { i++; sgn = -1; }
158 i = readdec(s, len, i, &val);
164 // Integer decimal power utility, with a tweak that enforces
165 // integer-exactness for arguments where that is possible.
166 static double decpow(int exp)
170 if(exp < 0 || exp >= DIGITS)
173 absexp = exp < 0 ? -exp : exp;
174 while(absexp--) v *= 10.0;
178 static int tonum(unsigned char* s, int len, double* result)
181 double sgn=1, val, frac=0, exp=0;
183 // Read the integer part
184 i = readsigned(s, len, i, &val);
185 if(val < 0) { sgn = -1; val = -val; }
187 // Read the fractional part, if any
188 if(i < len && s[i] == '.') {
190 fraclen = readdec(s, len, i, &frac) - i;
194 // Read the exponent, if any
195 if(i < len && (s[i] == 'e' || s[i] == 'E'))
196 i = readsigned(s, len, i+1, &exp);
198 // compute the result
199 *result = sgn * (val + frac * decpow(-fraclen)) * decpow(exp);
201 // if we didn't use the whole string, return failure
202 if(i < len) return 0;
206 // Very simple positive (!) integer print routine. Puts the result in
207 // s and returns the number of characters written. Does not null
208 // terminate the result.
209 static int decprint(int val, unsigned char* s)
212 if(val == 0) { *s = '0'; return 1; }
213 while(p <= val) p *= 10;
217 while(val >= p) { val -= p; count++; }
218 s[i++] = '0' + count;
224 // Takes a positive (!) floating point numbers, and returns exactly
225 // DIGITS decimal numbers in the buffer pointed to by s, and an
226 // integer exponent as the return value. For example, printing 1.0
227 // will result in "1000000000000000" in the buffer and -15 as the
228 // exponent. The caller can then place the floating point as needed.
229 static int rawprint(double val, unsigned char* s)
231 int exponent = (int)floor(log10(val));
232 double mantissa = val / pow(10, exponent);
234 for(i=0; i<DIGITS-1; i++) {
235 int digit = (int)floor(mantissa);
240 // Round (i.e. don't floor) the last digit
241 c = (int)floor(mantissa);
242 if(mantissa - c >= 0.5) c++;
246 return exponent - DIGITS + 1;
249 static int fromnum(double val, unsigned char* s)
251 unsigned char raw[DIGITS];
252 unsigned char* ptr = s;
256 if(val < 0) { *ptr++ = '-'; val = -val; }
258 // Exactly an integer is a special case
259 if(val == (int)val) {
260 ptr += decprint(val, ptr);
265 // Get the raw digits
266 exp = rawprint(val, raw);
268 // Examine trailing zeros to get a significant digit count
269 for(i=DIGITS-1; i>0; i--)
270 if(raw[i] != '0') break;
273 if(exp > 0 || exp < -(DIGITS+2)) {
274 // Standard scientific notation
279 for(i=1; i<digs; i++) *ptr++ = raw[i];
282 if(exp < 0) { exp = -exp; *ptr++ = '-'; }
283 else { *ptr++ = '+'; }
284 if(exp < 10) *ptr++ = '0';
285 ptr += decprint(exp, ptr);
286 } else if(exp < 1-DIGITS) {
287 // Fraction with insignificant leading zeros
288 *ptr++ = '0'; *ptr++ = '.';
289 for(i=0; i<-(exp+DIGITS); i++) *ptr++ = '0';
290 for(i=0; i<digs; i++) *ptr++ = raw[i];
293 for(i=0; i<DIGITS+exp; i++) *ptr++ = raw[i];
297 while(i<digs) *ptr++ = raw[i++];