Created
December 4, 2024 12:11
-
-
Save jonas-s-s-s/3af73229653f3fcd266697b78709b398 to your computer and use it in GitHub Desktop.
C++ dtoa no dependency
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Modified algorithm from: http://git.musl-libc.org/cgit/musl/tree/src/stdio/vfprintf.c#n208 | |
#include <cstdio> | |
namespace | |
{ | |
// default precision for floating-point conversion | |
constexpr int DEFAULT_PRECISION = 6; | |
inline void copy_str(char *dest, const char *src) | |
{ | |
// copy until null character | |
while ((*dest++ = *src++) != '\0') | |
; | |
} | |
template <typename T> | |
inline T max(T a, T b) | |
{ | |
return (a > b) ? a : b; | |
} | |
template <typename T> | |
inline T min(T a, T b) | |
{ | |
return (a < b) ? a : b; | |
} | |
// check if double is NaN | |
inline bool is_nan(double num) | |
{ | |
return num != num; | |
} | |
// check if double is infinity | |
inline bool is_inf(double num) | |
{ | |
return (num == num) && (num - num != 0.0); | |
} | |
// special cases (NaN, Infinity) | |
inline const char *check_special_cases(double num) | |
{ | |
if (is_nan(num)) | |
return "nan"; | |
if (is_inf(num)) | |
return (num < 0) ? "-inf" : "inf"; | |
return nullptr; | |
} | |
// reverse string in place | |
inline void reverse_str(char *str, int len) | |
{ | |
int i = 0, j = len - 1; | |
while (i < j) | |
{ | |
char temp = str[i]; | |
str[i] = str[j]; | |
str[j] = temp; | |
++i; | |
--j; | |
} | |
} | |
// convert integer to string | |
inline int int_to_str(int num, char *str, int min_width) | |
{ | |
int i = 0; | |
bool is_negative = (num < 0); | |
if (is_negative) | |
num = -num; | |
do | |
{ | |
str[i++] = '0' + (num % 10); | |
num /= 10; | |
} while (num > 0); | |
if (is_negative) | |
str[i++] = '-'; | |
// pad with zeros if necessary | |
while (i < min_width) | |
str[i++] = '0'; | |
str[i] = '\0'; | |
reverse_str(str, i); | |
return i; | |
} | |
} | |
// convert double to string | |
void dtoa(double num, char *buf, int precision, char format) | |
{ | |
char temp[32]; // buffer for intermediate results | |
int temp_len; | |
int int_part; | |
double frac_part; | |
int frac_len = 0; | |
int i = 0, j; | |
const char *special_case; | |
// special cases (NaN, Infinity) | |
special_case = check_special_cases(num); | |
if (special_case) | |
{ | |
copy_str(buf, special_case); | |
return; | |
} | |
// negative numbers | |
if (num < 0) | |
{ | |
buf[i++] = '-'; | |
num = -num; | |
} | |
// default precision | |
if (precision < 0) | |
precision = DEFAULT_PRECISION; | |
// extract integer and fractional parts | |
int_part = static_cast<int>(num); | |
frac_part = num - int_part; | |
// convert integer part to string | |
temp_len = int_to_str(int_part, temp, 0); | |
for (j = 0; j < temp_len; j++) | |
buf[i++] = temp[j]; | |
// fractional part | |
if (precision > 0) | |
{ | |
buf[i++] = '.'; // decimal point | |
while (precision-- > 0) | |
{ | |
frac_part *= 10; | |
int digit = static_cast<int>(frac_part); | |
buf[i++] = '0' + digit; | |
frac_part -= digit; | |
} | |
// rounding: check if remaining fraction >= 0.5 | |
if (frac_part >= 0.5) | |
{ | |
j = i - 1; | |
while (j >= 0 && buf[j] == '9') | |
{ | |
buf[j] = '0'; | |
j--; | |
} | |
if (j >= 0 && buf[j] != '.') | |
{ | |
buf[j]++; | |
} | |
else if (j < 0 || buf[j] == '.') | |
{ | |
// if overflow affects the integer part, insert '1' at the start | |
for (j = i; j > 0; j--) | |
buf[j] = buf[j - 1]; | |
buf[0] = '1'; | |
i++; | |
} | |
} | |
} | |
// null-terminate the string | |
buf[i] = '\0'; | |
// handle scientific/exponential notation | |
if (format == 'e' || format == 'E') | |
{ | |
int exponent = 0; | |
char sign; | |
// normalize the number to 1.xxxxx * 10^exponent | |
while (num >= 10.0) | |
{ | |
num /= 10.0; | |
exponent++; | |
} | |
while (num > 0.0 && num < 1.0) | |
{ | |
num *= 10.0; | |
exponent--; | |
} | |
// rebuild the string in scientific format | |
i = 0; | |
// reuse this function for normalized number | |
dtoa(num, buf, precision, '\0'); | |
// move to the end of the number | |
while (buf[i]) | |
i++; | |
// append 'e' or 'E' | |
buf[i++] = (format == 'e') ? 'e' : 'E'; | |
sign = (exponent < 0) ? '-' : '+'; | |
buf[i++] = sign; | |
// append the exponent | |
if (exponent < 0) | |
exponent = -exponent; | |
// at least 2 digits for exponent | |
temp_len = int_to_str(exponent, temp, 2); | |
for (j = 0; j < temp_len; j++) | |
buf[i++] = temp[j]; | |
// null-terminate | |
buf[i] = '\0'; | |
} | |
} | |
int main() | |
{ | |
char buffer[128]; | |
dtoa(1234.56789, buffer, 8, 'f'); // Fixed-point | |
printf("Fixed-point: %s\n", buffer); | |
dtoa(-0.000123456, buffer, 5, 'e'); // Scientific notation | |
printf("Scientific: %s\n", buffer); | |
dtoa(0.0 / 0.0, buffer, 6, 'f'); // NaN | |
printf("NaN: %s\n", buffer); | |
dtoa(1.0 / 0.0, buffer, 6, 'f'); // Infinity | |
printf("Infinity: %s\n", buffer); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment