printf 的浮点格式标志 (%f) 仅适用于英文数字格式

Posted

技术标签:

【中文标题】printf 的浮点格式标志 (%f) 仅适用于英文数字格式【英文标题】:printf's floating point format flag (%f) for English number format only 【发布时间】:2018-02-24 22:45:01 【问题描述】:

我正在将数据写入一个可以在多台计算机上打开的文件。问题是,当我使用“%f”格式化标志格式化浮点数时,函数的printf 变体(wprintfCString::FormatStringCchPrintf 等)使其成为特定于语言环境的变量。

例如:

double f = 1.5;
WCHAR buff[256];
StringCchPrintf(buff, 256, L"%f", f);

将为美国英语输出"1.5",但随后可能为某些欧洲语言环境等输出"1,5"

所以我很好奇除了%f 之外是否还有一个标志只会以一种格式输出浮点数? (最好是英文。)

【问题讨论】:

不,没有。 @NeilButterworth:那你是做什么的?编写您自己的从浮点到字符串的转换例程,或者在每次 printf 调用之前更改和恢复语言环境? 编写一个包装 printf 的函数,它可以透明地更改和恢复语言环境。或者在程序开始时更改一次区域设置。 【参考方案1】:

考虑到这个问题被标记为 C++:使用 std::num_putstd::numpunct 中的格式设置为 ”C” 语言环境。最简单的方法是使用合适的std::ostreamimbue()ing std::locale::classic()

评论建议编写自己的转换函数:我建议不要这样做,因为正确实现此转换并非易事。见How to Print Floating-Point Numbers Accurately

【讨论】:

如果您指的是我的评论,我不建议重写 printf,正如您所说的那样,这将是一项重大任务,我建议将其包装以管理语言环境的更改。 @NeilButterworth:MikeF 询问“编写你自己的从浮点到字符串的转换例程”。【参考方案2】:

您需要在致电StringCchPrintf 或说_snwprintf 之前致电setlocale_wsetlocale。例如:

    WCHAR buf[32];
    double f = 1.5;

    setlocale(LC_NUMERIC, "english");
    _snwprintf(buf, RTL_NUMBER_OF(buf), L"%f", f);
    // 1.500000
    setlocale(LC_NUMERIC, "German");
    _snwprintf(buf, RTL_NUMBER_OF(buf), L"%f", f);
    // 1,500000

或者作为替代可以使用StringCchPrintf_l或其他_l函数。但在这种情况下,您需要致电 _create_locale_free_locale

    WCHAR buf[32];
    double f = 1.5;
    _locale_t locale;

    if (locale = _create_locale(LC_NUMERIC, "German"))
    
        _snwprintf_l(buf, RTL_NUMBER_OF(buf), L"%f", locale, f);
        _free_locale(locale);
        // 1,500000
    

    if (locale = _create_locale(LC_NUMERIC, "english"))
    
        _snwprintf_l(buf, RTL_NUMBER_OF(buf), L"%f", locale, f);
        _free_locale(locale);
        // 1.500000
    

我们可以使用例如下一个Language Strings。但是存在多种使用语言环境字符串的方法

【讨论】:

setlocale(默认情况下)为进程中的所有线程设置区域设置,从而更改不属于您的线程的运行时行为。 SetThreadLocale 是一个更好的选择。或者,使用_configthreadlocale 启用每线程区域设置。 @IInspectable - 或者我们可以通过_create_locale 创建语言环境并明确使用它,就像我在代码中显示的那样 我在评论您提出的第一个解决方案,它不太理想,并且会引入微妙的(而不是那么微妙的)错误。如果您必须更改语言环境,则仅对调用线程执行此操作。根本不改变语言环境通常是一个更好的主意。但是,仅显示第二个解决方案并不能解决第一个解决方案的问题。 @IInspectable - 这取决于 - 哪个代码调用 setlocale。如果来自 exe 的代码 - 这没关系。 exe代码有权这样做。如果来自通用 dll - 这已经不行了,并且真的会影响进程。 这也不能从 EXE 调用。它改变了你不拥有或控制的线程的环境。我已经解释了问题是什么,并提出了两种解决方法。【参考方案3】:

在程序中尝试以下操作:

setlocale (LC_ALL, "en_US.UTF-8");

或者在程序启动前设置环境变量:

export LC_NUMERIC="en_US.UTF-8";

【讨论】:

以上是关于printf 的浮点格式标志 (%f) 仅适用于英文数字格式的主要内容,如果未能解决你的问题,请参考以下文章

printf%f如何在32位浮点数上运行

格式化输出输入

在floatf格式说明符和pow函数中C浮点或双精度

printf格式化输出

C零基础课程-06-printf进阶

Linux上printf命令的用法