/**
* Formats and prints a number with configurable thousand separators, right aligned
* decimals: Number of decimal digits, default 0
* width: Overall width of the resulting string (string will be at least as wide
* as number requires, though)
* pos_leading_char: First character in front of positive numbers, or \0 (default)
* for none. Meaningful values are ' ' and '+'
* thousand_sep: Separator char between triplets of whole number part (default ',')
* decimal_sep: Decimal separator (default '.', unless thousand_sep is set to '.',
* in which case default is ',')
*/
void printf_num(var number, int decimals, int width, char pos_leading_char, char thousand_sep, char decimal_sep) {
#define MAX_WIDTH 30
if(decimals < 0 || decimals > MAX_WIDTH-4 || width > MAX_WIDTH-1) return;
char format_string[7];
char buffer[MAX_WIDTH];
char result[MAX_WIDTH];
sprintf(format_string, "%%+.%02if", decimals); // Create format string with right number of decimals
sprintf(buffer, format_string, number); // Format number without thousands separators
int input_pointer = strlen(buffer);
int offset = 2 + (1 && decimals); // 2 if decimals == 0, otherwise 3
// Calculate length of result string with thousands separators after every 3 digits
int new_len = input_pointer + (int)((input_pointer - decimals - offset)/3);
if(number >= 0 && !pos_leading_char) { // Subtract 1 if there is no leading character
new_len--;
}
// Expand to width, but no more than MAX_WIDTH. Subtract 1 because of 0 based counting
int result_pointer = min(max(new_len, width) + 1, MAX_WIDTH) - 1;
// Fill resulting string from right to left
int digit_counter = -1;
while(result_pointer >= 0) {
if(input_pointer >= 0) {
if(buffer[input_pointer] == '.') {
result[result_pointer--] = decimal_sep; // Insert decimal separator and
input_pointer--;
digit_counter = 0; // start counting digits left of decimal point
} else if(buffer[input_pointer] == '+') { // Replace leading '+' as the case may be
if(pos_leading_char) {
result[result_pointer--] = pos_leading_char;
} else {
result[result_pointer--] = ' ';
}
input_pointer--;
} else if(digit_counter == 3 && input_pointer > 0) {
result[result_pointer--] = thousand_sep; // Add thousands separator every 3rd digit
digit_counter = 0;
} else { // Otherwise, just copy formatted number incl. final \0
result[result_pointer--] = buffer[input_pointer--];
if(digit_counter >= 0 || decimals == 0) digit_counter++;
}
} else {
result[result_pointer--] = ' '; // Fill the rest (left side) with blanks
}
}
printf("%s", result);
}
// Overloaded function signatures to simulate default values
void printf_num(var number) {
printf_num(number, 0, 0, '\0', ',', '.');
}
void printf_num(var number, int decimals) {
printf_num(number, decimals, 0, '\0', ',', '.');
}
void printf_num(var number, int decimals, int width) {
printf_num(number, decimals, width, '\0', ',', '.');
}
void printf_num(var number, int decimals, int width, char pos_leading_char) {
printf_num(number, decimals, width, pos_leading_char, ',', '.');
}
void printf_num(var number, int decimals, int width, char pos_leading_char, char thousand_sep) {
char decimal_sep = '.';
if(thousand_sep == '.') {
decimal_sep = ',';
}
printf_num(number, decimals, width, pos_leading_char, thousand_sep, decimal_sep);
}