Posted By: Morris
Number formatting - 01/04/21 21:43
I haven't seen a facility in Zorro to print large numbers more legibly with thousands separators, so I implemented one. Maybe this is useful for those of you dealing with larger figures :-) Should be rather self-explanatory, and can of course be easily adjusted to return a string instead:
Code
/** * 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); }