aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/irrlicht-1.8/include/fast_atof.h
blob: d11f7b4956733535d0321d3ca806cfbeb83cb1cf (plain)
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine" and the "irrXML" project.
// For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h

#ifndef __FAST_ATOF_H_INCLUDED__
#define __FAST_ATOF_H_INCLUDED__

#include "irrMath.h"
#include "irrString.h"

namespace irr
{
namespace core
{
	//! Selection of characters which count as decimal point in fast_atof
	// TODO: This should probably also be used in irr::core::string, but the float-to-string code
	//		used there has to be rewritten first.
	IRRLICHT_API extern irr::core::stringc LOCALE_DECIMAL_POINTS;

// we write [17] here instead of [] to work around a swig bug
const float fast_atof_table[17] = {
	0.f,
	0.1f,
	0.01f,
	0.001f,
	0.0001f,
	0.00001f,
	0.000001f,
	0.0000001f,
	0.00000001f,
	0.000000001f,
	0.0000000001f,
	0.00000000001f,
	0.000000000001f,
	0.0000000000001f,
	0.00000000000001f,
	0.000000000000001f,
	0.0000000000000001f
};

//! Convert a simple string of base 10 digits into an unsigned 32 bit integer.
/** \param[in] in: The string of digits to convert. No leading chars are
    allowed, only digits 0 to 9. Parsing stops at the first non-digit.
    \param[out] out: (optional) If provided, it will be set to point at the
    first character not used in the calculation.
    \return The unsigned integer value of the digits. If the string specifies
    too many digits to encode in an u32 then INT_MAX will be returned.
*/
inline u32 strtoul10(const char* in, const char** out=0)
{
	if (!in)
	{
		if (out)
			*out = in;
		return 0;
	}

	bool overflow=false;
	u32 unsignedValue = 0;
	while ( ( *in >= '0') && ( *in <= '9' ))
	{
		const u32 tmp = ( unsignedValue * 10 ) + ( *in - '0' );
		if (tmp<unsignedValue)
		{
			unsignedValue=(u32)0xffffffff;
			overflow=true;
		}
		if (!overflow)
			unsignedValue = tmp;
		++in;
	}

	if (out)
		*out = in;

	return unsignedValue;
}

//! Convert a simple string of base 10 digits into a signed 32 bit integer.
/** \param[in] in: The string of digits to convert. Only a leading - or +
    followed by digits 0 to 9 will be considered. Parsing stops at the first
    non-digit.
    \param[out] out: (optional) If provided, it will be set to point at the
    first character not used in the calculation.
    \return The signed integer value of the digits. If the string specifies
    too many digits to encode in an s32 then +INT_MAX or -INT_MAX will be
    returned.
*/
inline s32 strtol10(const char* in, const char** out=0)
{
	if (!in)
	{
		if (out)
			*out = in;
		return 0;
	}

	const bool negative = ('-' == *in);
	if (negative || ('+' == *in))
		++in;

	const u32 unsignedValue = strtoul10(in,out);
	if (unsignedValue > (u32)INT_MAX)
	{
		if (negative)
			return (s32)INT_MIN;
		else
			return (s32)INT_MAX;
	}
	else
	{
		if (negative)
			return -((s32)unsignedValue);
		else
			return (s32)unsignedValue;
	}
}

//! Convert a hex-encoded character to an unsigned integer.
/** \param[in] in The digit to convert. Only digits 0 to 9 and chars A-F,a-f
    will be considered.
    \return The unsigned integer value of the digit. 0xffffffff if the input is
    not hex
*/
inline u32 ctoul16(char in)
{
	if (in >= '0' && in <= '9')
		return in - '0';
	else if (in >= 'a' && in <= 'f')
		return 10u + in - 'a';
	else if (in >= 'A' && in <= 'F')
		return 10u + in - 'A';
	else
		return 0xffffffff;
}

//! Convert a simple string of base 16 digits into an unsigned 32 bit integer.
/** \param[in] in: The string of digits to convert. No leading chars are
    allowed, only digits 0 to 9 and chars A-F,a-f are allowed. Parsing stops
    at the first illegal char.
    \param[out] out: (optional) If provided, it will be set to point at the
    first character not used in the calculation.
    \return The unsigned integer value of the digits. If the string specifies
    too many digits to encode in an u32 then INT_MAX will be returned.
*/
inline u32 strtoul16(const char* in, const char** out=0)
{
	if (!in)
	{
		if (out)
			*out = in;
		return 0;
	}

	bool overflow=false;
	u32 unsignedValue = 0;
	while (true)
	{
		u32 tmp = 0;
		if ((*in >= '0') && (*in <= '9'))
			tmp = (unsignedValue << 4u) + (*in - '0');
		else if ((*in >= 'A') && (*in <= 'F'))
			tmp = (unsignedValue << 4u) + (*in - 'A') + 10;
		else if ((*in >= 'a') && (*in <= 'f'))
			tmp = (unsignedValue << 4u) + (*in - 'a') + 10;
		else
			break;
		if (tmp<unsignedValue)
		{
			unsignedValue=(u32)INT_MAX;
			overflow=true;
		}
		if (!overflow)
			unsignedValue = tmp;
		++in;
	}

	if (out)
		*out = in;

	return unsignedValue;
}

//! Convert a simple string of base 8 digits into an unsigned 32 bit integer.
/** \param[in] in The string of digits to convert. No leading chars are
    allowed, only digits 0 to 7 are allowed. Parsing stops at the first illegal
    char.
    \param[out] out (optional) If provided, it will be set to point at the
    first character not used in the calculation.
    \return The unsigned integer value of the digits. If the string specifies
    too many digits to encode in an u32 then INT_MAX will be returned.
*/
inline u32 strtoul8(const char* in, const char** out=0)
{
	if (!in)
	{
		if (out)
			*out = in;
		return 0;
	}

	bool overflow=false;
	u32 unsignedValue = 0;
	while (true)
	{
		u32 tmp = 0;
		if ((*in >= '0') && (*in <= '7'))
			tmp = (unsignedValue << 3u) + (*in - '0');
		else
			break;
		if (tmp<unsignedValue)
		{
			unsignedValue=(u32)INT_MAX;
			overflow=true;
		}
		if (!overflow)
			unsignedValue = tmp;
		++in;
	}

	if (out)
		*out = in;

	return unsignedValue;
}

//! Convert a C-style prefixed string (hex, oct, integer) into an unsigned 32 bit integer.
/** \param[in] in The string of digits to convert. If string starts with 0x the
    hex parser is used, if only leading 0 is used, oct parser is used. In all
    other cases, the usual unsigned parser is used.
    \param[out] out (optional) If provided, it will be set to point at the
    first character not used in the calculation.
    \return The unsigned integer value of the digits. If the string specifies
    too many digits to encode in an u32 then INT_MAX will be returned.
*/
inline u32 strtoul_prefix(const char* in, const char** out=0)
{
	if (!in)
	{
		if (out)
			*out = in;
		return 0;
	}
	if ('0'==in[0])
		return ('x'==in[1] ? strtoul16(in+2,out) : strtoul8(in+1,out));
	return strtoul10(in,out);
}

//! Converts a sequence of digits into a whole positive floating point value.
/** Only digits 0 to 9 are parsed.  Parsing stops at any other character,
    including sign characters or a decimal point.
    \param in: the sequence of digits to convert.
    \param out: (optional) will be set to point at the first non-converted
    character.
    \return The whole positive floating point representation of the digit
    sequence.
*/
inline f32 strtof10(const char* in, const char** out = 0)
{
	if (!in)
	{
		if (out)
			*out = in;
		return 0.f;
	}

	const u32 MAX_SAFE_U32_VALUE = UINT_MAX / 10 - 10;
	u32 intValue = 0;

	// Use integer arithmetic for as long as possible, for speed
	// and precision.
	while ( ( *in >= '0') && ( *in <= '9' ) )
	{
		// If it looks like we're going to overflow, bail out
		// now and start using floating point.
		if (intValue >= MAX_SAFE_U32_VALUE)
			break;

		intValue = (intValue * 10) + (*in - '0');
		++in;
	}

	f32 floatValue = (f32)intValue;

	// If there are any digits left to parse, then we need to use
	// floating point arithmetic from here.
	while ( ( *in >= '0') && ( *in <= '9' ) )
	{
		floatValue = (floatValue * 10.f) + (f32)(*in - '0');
		++in;
		if (floatValue > FLT_MAX) // Just give up.
			break;
	}

	if (out)
		*out = in;

	return floatValue;
}

//! Provides a fast function for converting a string into a float.
/** This is not guaranteed to be as accurate as atof(), but is
    approximately 6 to 8 times as fast.
    \param[in] in The string to convert.
    \param[out] result The resultant float will be written here.
    \return Pointer to the first character in the string that wasn't used
    to create the float value.
*/
inline const char* fast_atof_move(const char* in, f32& result)
{
	// Please run the regression test when making any modifications to this function.

	result = 0.f;
	if (!in)
		return 0;

	const bool negative = ('-' == *in);
	if (negative || ('+'==*in))
		++in;

	f32 value = strtof10(in, &in);

	if ( LOCALE_DECIMAL_POINTS.findFirst(*in) >= 0 )
	{
		const char* afterDecimal = ++in;
		const f32 decimal = strtof10(in, &afterDecimal);
		value += decimal * fast_atof_table[afterDecimal - in];
		in = afterDecimal;
	}

	if ('e' == *in || 'E' == *in)
	{
		++in;
		// Assume that the exponent is a whole number.
		// strtol10() will deal with both + and - signs,
		// but calculate as f32 to prevent overflow at FLT_MAX
		value *= powf(10.f, (f32)strtol10(in, &in));
	}

	result = negative?-value:value;
	return in;
}

//! Convert a string to a floating point number
/** \param floatAsString The string to convert.
    \param out Optional pointer to the first character in the string that
    wasn't used to create the float value.
    \result Float value parsed from the input string
*/
inline float fast_atof(const char* floatAsString, const char** out=0)
{
	float ret;
	if (out)
		*out=fast_atof_move(floatAsString, ret);
	else
		fast_atof_move(floatAsString, ret);
	return ret;
}

} // end namespace core
} // end namespace irr

#endif