如何在Windows上用C语言为double类型添加千位分隔符?

6

我使用MPFR库对大数进行计算,但同时返回一个保留8位小数的双精度浮点数。

我将数字用mpfr_sprintf格式化为char数组以保留精度等信息。但是,我在文档中没有找到任何千位分隔符选项(或者我可能错过了它)。

例如,给定一个数字20043.95381376,我想要表示为20,043.95381376,以便更好地阅读。

或者数字164992818.48075795表示为164,992,818.48075795

我了解到可以在printf / sprintf中添加撇号,但似乎这只适用于UNIX / POSIX系统,而我是Windows用户。

由于我在内部将数字打印为字符串,所以我想到的解决方法是编写自定义实现,根据数字(> 1000> 10000> 100000等)自动添加逗号,但然后我意识到像strncpy或strcpy之类的函数实际上会替换而不是添加逗号到所需位置。这就是我如何回到原点的原因。

我该怎么做?


这个问题是关于整数的,但这个链接可能会有所帮助:http://stackoverflow.com/q/4109006/10077 - Fred Larson
我尝试了这个函数,但是失去了精度,只得到了小数点后2位数字,而不是我的原始8位。 - farmdve
您可以通过localeconv获取字符使用和分组方式的信息(该函数是C89标准,因此我假设在Windows下可用),但我不知道有什么标准函数会使用它。 - AProgrammer
对于整数,还有一个相关的问题,可以在这里找到:https://dev59.com/QHM_5IYBdhLWcg3wRw51 - Roland Pihlakas
不要使用strncpy,它的语义反直觉且容易出错。 - chqrlie
4个回答

3
你需要实现将双精度浮点数转换为字符串并检查该字符串中的每个字符,然后将其与分隔符一起复制到输出字符串中。
大致如下:
#include <stdio.h>
#include <string.h>

int thousandsep(double in, char* out_str, size_t out_len, unsigned int precision) {
    char in_str[128], int_str[128], format[32];
    size_t dlen, mod, i, j;
    int c;

    snprintf(format, sizeof format, "%%.%df", precision);
    snprintf(in_str, sizeof in_str, format, in);
    snprintf(int_str, sizeof int_str, "%d", (int)in);

    dlen = strlen(in_str);
    mod = strlen(int_str) % 3;
    c = (mod == 0) ? 3 : mod;

    for (i=0, j=0; i<dlen; i++, j++, c--) {
        if (j >= out_len - 1) {
            /* out_str is too small */
            return -1;
        }

        if (in_str[i] == '.') {
            c = -1;
        } else if (c == 0) {
            out_str[j++] = ',';
            c = 3;
        }

        out_str[j] = in_str[i];
    }
    out_str[j] = '\0';

    return 0;
}

然后像这样使用它:
char out_str[64];

if (thousandsep(20043.95381376, out_str, sizeof out_str, 8) == 0)
    printf("%s\n", out_str);       /* 20,043.95381376 */

if (thousandsep(164992818.48075795, out_str, sizeof out_str, 8) == 0)
    printf("%s\n", out_str);       /* 164,992,818.48075795 */

if (thousandsep(1234567.0, out_str, sizeof out_str, 0) == 0)
    printf("%s\n", out_str);       /* 1,234,567 */

注意:我假设您正在使用Windows,可能正在使用MSVC,因此此解决方案应在C89编译器上工作。


这对于精度为3的999.9999不起作用,因为转换将产生1000.000,但(int)in将是999 - chqrlie
你应该删除前两个 snprintf 调用,并写入 snprintf(in_str, sizeof in_str, "%.*f", (int)precision, in); - chqrlie
第三个 snprintf 应该被删除,并且 mod 可以通过计算 mod = strcspn(in_str, ".") % 3; 来获得。 - chqrlie

2

GetNumberFormatEx将获取纯文本字符串版本的数字,并使用分组分隔符、适当的小数点等进行格式化。将LOCALE_NAME_USER_DEFAULT作为区域设置参数传递,它将以用户喜好的格式显示。

如果您需要覆盖其中一个设置(如精度),则可以使用默认值填充NUMBERFMT结构体,然后更改需要控制的字段。


0

似乎没有可用的格式指令。

这是一种快速而简单的方法,可以将包含浮点数的字符串插入适当位置的逗号。

这使用了几个临时缓冲区。千位分隔符号将取决于语言环境,小数点符号也是如此。但是,在此示例中,逗号是硬编码的。

基本上,它只是将浮点数的字符串表示形式复制到另一个缓冲区中,并在适当的位置插入逗号。

您还可以查看使用较少缓冲区来完成此操作,因为像我说的那样,这很快而简单,但效率不高。

{
    double dFloat = 123456789012.567890;
    char xBuff[128];
    sprintf (xBuff, "%f", dFloat);

    char xBuff2[128];
    int iLen = strlen(xBuff);
    int iPoint = iLen;
    for (iLen--; iLen >= 0; iLen--) {
        if (xBuff[iLen] == '.' || xBuff[iLen] == ',') {
            // found the decimal point.  depends on locale.
            iPoint = iLen;
            break;
        }
    }
    strcpy (xBuff2, xBuff + iPoint);   // save the decimal portion

    char xBuff3[128], xBuff4[128];
    xBuff3[127] = 0;    // set an end of string
    int  iCount, jLen;
    for (iCount = 1, jLen = 126, iLen--; iLen >= 0; jLen--, iLen--) {
        if ((iCount % 4) == 0) {
            xBuff3[jLen] = ',';
            jLen--;
            iCount = 1;
        }
        xBuff3[jLen] = xBuff[iLen];
        iCount++;
    }
    strcpy (xBuff4, xBuff3 + jLen + 1);
    strcat (xBuff4, xBuff2);
}

0
#include <stdio.h>
#include <locale.h>

int main(void)
{
    setlocale(LC_NUMERIC, "");
    printf("%'.8lf\n", 164992818.48075795);
    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接