Created
December 4, 2024 11:37
-
-
Save jonas-s-s-s/9974a79ce7d9b7e7ac1b51f9ea4e1de8 to your computer and use it in GitHub Desktop.
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
// Logic form: http://git.musl-libc.org/cgit/musl/tree/src/stdio/vfprintf.c#n208 | |
// Example program | |
#include <cstdio> | |
#include <math.h> | |
#include <limits.h> | |
#define MAX(a, b) ((a) > (b) ? (a) : (b)) | |
#define MIN(a, b) ((a) < (b) ? (a) : (b)) | |
#define DEFAULT_PRECISION 6 // Default precision for floating-point conversion | |
// Helper to check if a number is NaN or Infinity | |
static const char *check_special_cases(double num) { | |
if (isnan(num)) return "nan"; | |
if (isinf(num)) return (num < 0) ? "-inf" : "inf"; | |
return NULL; | |
} | |
// Helper to reverse a string in place | |
static void reverse_str(char *str, int len) { | |
int i, j; | |
char temp; | |
for (i = 0, j = len - 1; i < j; i++, j--) { | |
temp = str[i]; | |
str[i] = str[j]; | |
str[j] = temp; | |
} | |
} | |
// Helper to convert an integer to string | |
static int int_to_str(int num, char *str, int min_width) { | |
int i = 0; | |
int 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) { | |
while (*special_case) buf[i++] = *special_case++; | |
buf[i] = '\0'; | |
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 = (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 = (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