Created
December 4, 2024 11:44
-
-
Save jonas-s-s-s/c96e808aa0e1a83de4dc301f10877c74 to your computer and use it in GitHub Desktop.
C++ style dtoa / double to string
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
// 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