Skip to content

Instantly share code, notes, and snippets.

@jonas-s-s-s
Created December 4, 2024 11:44
Show Gist options
  • Save jonas-s-s-s/c96e808aa0e1a83de4dc301f10877c74 to your computer and use it in GitHub Desktop.
Save jonas-s-s-s/c96e808aa0e1a83de4dc301f10877c74 to your computer and use it in GitHub Desktop.
C++ style dtoa / double to string
// Example program
#include <cstdio>
#include <cstring>
// Replacing macros with constexpr or inline functions
constexpr int DEFAULT_PRECISION = 6; // Default precision for floating-point conversion
// Inline functions for max and min
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;
}
// Function to check if a double is NaN
inline bool is_nan(double num) {
// NaN is not equal to itself
return num != num;
}
// Function to check if a double is infinity
inline bool is_inf(double num) {
// Infinity divided by itself gives NaN
return (num == num) && (num - num != 0.0);
}
// Helper to handle 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;
}
// Helper to reverse a 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;
}
}
// Helper to convert an 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;
}
// Core function to convert double to string
void fmt_fp_to_str(double num, char* buf, int precision, char format) {
char temp[32]; // Temporary 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;
// Handle special cases (NaN, Infinity)
special_case = check_special_cases(num);
if (special_case) {
std::strcpy(buf, special_case);
return;
}
// Handle 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];
// Handle 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;
}
// Simple 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 specified
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;
fmt_fp_to_str(num, buf, precision, '\0'); // Reuse this function for normalized number
while (buf[i]) i++; // Move to the end of the number
buf[i++] = (format == 'e') ? 'e' : 'E'; // Append 'e' or 'E'
sign = (exponent < 0) ? '-' : '+';
buf[i++] = sign;
// Append the exponent
if (exponent < 0) exponent = -exponent;
temp_len = int_to_str(exponent, temp, 2); // At least 2 digits for exponent
for (j = 0; j < temp_len; j++) buf[i++] = temp[j];
buf[i] = '\0'; // Null-terminate
}
}
int main() {
char buffer[128];
fmt_fp_to_str(1234.56789, buffer, 8, 'f'); // Fixed-point
printf("Fixed-point: %s\n", buffer);
fmt_fp_to_str(-0.000123456, buffer, 5, 'e'); // Scientific notation
printf("Scientific: %s\n", buffer);
fmt_fp_to_str(0.0 / 0.0, buffer, 6, 'f'); // NaN
printf("NaN: %s\n", buffer);
fmt_fp_to_str(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