/* * comma - convert numbers into strings with 3-digit group and integer-fraction separators * * Convert numbers into strings with 3-digit group and integer-fraction separators. * * If the value is an integer, the integer-fraction separator is not used. * * str_comma(x, [group, [decimal]]) * * Convert x into a string. * * If group is given and is a string, group will be used as * the 3-digit group separator, otherwise the default 3-digit * group separator will be used. * * If decimal is given and is a string, group will be used as * the integer-fraction separator, otherwise the default * integer-fraction separator will be used. * * The decimal and group arguments are optional. * * set_default_group_separator(group) * * Change the default 3-digit group separator if group is a string, * otherwise the default 3-digit group separator will not be * changed. Return the old 3-digit group separator. * * set_default_decimal_separator(decimal) * * Change the default 3-digit group separator if decimal is a * string, otherwise the default integer-fraction separator * will not be changed. Return the old integer-fraction separator. * * print_comma(x, [group, [decimal]]) * * Print the value produced by str_comma(x, [group, [decimal]]) * followed by a newline. * * If the str_comma() does not return a string, nothing is printed. * * The decimal and group arguments are optional. * * The value produced by str_comma() is returned. * * fprint_comma(fd, x, [group, [decimal]]) * * Print the value produced by str_comma(x, [group, [decimal]]), * without a trailing newline, on file fd. * * If the str_comma() does not return a string, nothing is printed. * * If fd is not an open file, nothing is printed. * * The decimal and group arguments are optional. * * The value produced by str_comma() is returned. * * Copyright (C) 2022 Landon Curt Noll * * Calc is open software; you can redistribute it and/or modify it under * the terms of the version 2.1 of the GNU Lesser General Public License * as published by the Free Software Foundation. * * Calc is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General * Public License for more details. * * A copy of version 2.1 of the GNU Lesser General Public License is * distributed with calc under the filename COPYING-LGPL. You should have * received a copy with calc; if not, write to Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Under source code control: 2022/06/20 15:51:49 * File existed as early as: 2022 * * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/ */ static default_group_separator = ","; /* default 3-digit group separator */ static default_decimal_separator = "."; /* default integer-fraction separator */ /* * str_comma - convert number into base 10 string with 3-digit groups and integer-fraction separator * * * This function converts a real number into a base 10 string, where * groups of 3 digits are separated by a 3-digit group separator and * a separator is printed between integer and decimal fraction. * * For example: * * string = str_comma(x); * string = str_comma(x), " ", "."); * string = str_comma(x), ".", ","); * * Internally the function calls: * * strprintf("%f", x); * * and thus the number of decimal fraction digits is subject to * the display() or config("display") value. See: * * man display * * for details. * * given: * * x number to convert * * optional args: * * group 3-digit group separator (def: ",") * decimal separator between decimal integer and decimal fraction (def: ".") * * returns: * * string containing the base 10 digits with group and decimal separators, OR * null() if x is not a number, OR * null() if group is neither null() (not given) nor a string, OR * null() if group is null() (not given) AND default_group_separator is not a string, OR * null() if decimal is neither null() (not given) nor a string, OR * null() if decimal is null() (not given) AND default_decimal_separator is not a string. */ define str_comma(x, group, decimal) { local group_separator; /* 3-digit group separator */ local decimal_separator; /* separator between decimal integer and decimal fraction */ local sign_str; /* leading - if x < 0 or empty if x >= 0 */ local integer; /* integer part of absolute value of x */ local int_str; /* integer as a string */ local int_len; /* number of digits in int_str */ local first_group_len; /* length of 1st group before the 1st 3-digit group separator */ local fraction; /* factional part of absolute value of x */ local frac_str; /* fraction as a string */ local frac_len; /* number of digits in frac_str including leading 0. */ local ret; /* string to return */ local i; /* * parse args - return null if args are bogus * * Return null() if args or conditions are bogus. */ if (!isreal(x)) { return null(); } group_separator = isnull(group) ? default_group_separator : group; decimal_separator = isnull(decimal) ? default_decimal_separator : decimal; if (!isstr(group_separator)) { return null(); } if (!isstr(decimal_separator)) { return null(); } /* * split number */ if (x < 0) { sign_str = "-"; integer = int(-x); fraction = frac(-x); } else { sign_str = ""; integer = int(x); fraction = frac(x); } ret = sign_str; /* * convert digits */ int_str = strprintf("%d", integer); frac_str = strprintf("%d", fraction); /* * determine number of digits in the integer part */ int_len = strlen(int_str); frac_len = strlen(frac_str); /* * form integer part with group separators as needed */ /* * case: integer is 3 or fewer digits */ if (integer < 1000) { ret += int_str; /* * case: integer is 4 or more digits */ } else { /* * form a decimal string using group separators */ /* * form the initial leading digits before 1st group separator */ first_group_len = int_len % 3; if (first_group_len == 0) { first_group_len = 3; } ret += substr(int_str, 1, first_group_len); /* * until end of digits, print group separator followed by 3 more digits */ for (i = first_group_len+1; i < int_len; i += 3) { ret += group_separator + substr(int_str, i, 3); } } /* * form fractional part using decimal separator as needed */ /* * case: x is an integer */ if (fraction == 0) { /* no fraction, nothing more to do */ /* * case: x is not an integer */ } else { /* * add separator */ ret += decimal_separator; /* * add remaining digits * * Skip over the leading 0. in frac_str */ ret += substr(frac_str, 3, frac_len-2); } /* * All Done!!! -- Jessica Noll, Age 2 */ return ret; } /* * set_default_group_separator - change the default integer-fraction separator * * If group is not a string, then the default 3-digit group separator * is not changed. Thus, this will only return the default 3-digit group separator: * * set_default_group_separator(null()); * * given: * * group 3-digit group separator * * returns: * * previous 3-digit group separator value */ define set_default_group_separator(group) { local old_default_group_separator; /* previous default 3-digit group separator to return */ /* * save current 3-digit group separator */ old_default_group_separator = default_group_separator; /* * change 3-digit group separator is group is a string */ if (isstr(group)) { default_group_separator = group; } return old_default_group_separator; } /* * set_default_decimal_separator - change the default 3-digit decimal separator * * If decimal is not a string, then the default 3-digit decimal separator * is not changed. Thus, this will only return the default 3-digit decimal separator: * * set_default_decimal_separator(null()); * * given: * * decimal separator between decimal integer and decimal fraction (def: ".") * * returns: * * previous 3-digit decimal separator value */ define set_default_decimal_separator(decimal) { local old_default_decimal_separator; /* previous default 3-digit decimal separator */ /* * save current 3-digit decimal separator */ old_default_decimal_separator = default_decimal_separator; /* * change 3-digit decimal separator is decimal is a string */ if (isstr(decimal)) { default_decimal_separator = decimal; } return old_default_decimal_separator; } /* * print_comma - print base 10 string with 3-digit group separators & integer-fraction separator + newline * * This function prints the result of str_comma(x, group, decimal) followed by a newline. * For example: * * print_comma(x); * print_comma(x), " ", "."); * print_comma(x), ".", ","); * * If str_comma() does not return a string, this function prints nothing. * * NOTE: To print without a newline, use fprint_comma(fd, x, group, decimal). * * given: * x number to convert * * optional args: * * group 3-digit group separator (def: ",") * decimal separator between decimal integer and decimal fraction (def: ".") * * returns: * * string containing the base 10 digits with group and decimal separators, OR * null() if x is not a number, OR * null() if group is neither null() (not given) nor a string, OR * null() if group is null() (not given) AND default_group_separator is not a string, OR * null() if decimal is neither null() (not given) nor a string, OR * null() if decimal is null() (not given) AND default_decimal_separator is not a string. */ define print_comma(x, group, decimal) { local ret; /* base 10 string with 3-digit group separators /* * convert to string */ ret = str_comma(x, group, decimal); /* * print converted string */ if (isstr(ret)) { printf("%s\n", ret); } return ret; } /* * print_comma - print base 10 string with 3-digit group separators & integer-fraction separator w/o newline * * This function prints the result of str_comma(x, group, decimal) on an open file, without a trailing newline. * For example: * * fprint_comma(files(1), x); * fprint_comma(fd, x), " ", "."); * fprint_comma(files(2), x), ".", ","); * * If str_comma() does not return a string, this function prints nothing. * * NOTE: To print with a newline, use print_comma(x, group, decimal). * * given: * fd open file * x number to convert * * optional args: * * group 3-digit group separator (def: ",") * decimal separator between decimal integer and decimal fraction (def: ".") * * returns: * * string containing the base 10 digits with group and decimal separators, OR * null() if x is not a number, OR * null() if group is neither null() (not given) nor a string, OR * null() if group is null() (not given) AND default_group_separator is not a string, OR * null() if decimal is neither null() (not given) nor a string, OR * null() if decimal is null() (not given) AND default_decimal_separator is not a string. */ define fprint_comma(fd, x, group, decimal) { local ret; /* base 10 string with 3-digit group separators /* * convert to string */ ret = str_comma(x, group, decimal); /* * print converted string */ if (isstr(ret) && isfile(fd)) { fprintf(fd, "%s\n", ret); } return ret; }