Skip to content

Instantly share code, notes, and snippets.

@jonas-s-s-s
Created December 4, 2024 12:11
Show Gist options
  • Save jonas-s-s-s/3af73229653f3fcd266697b78709b398 to your computer and use it in GitHub Desktop.
Save jonas-s-s-s/3af73229653f3fcd266697b78709b398 to your computer and use it in GitHub Desktop.
C++ dtoa no dependency
// 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