【问题标题】:Use sprintf to format floats with no decimal places if integer如果为整数,则使用 sprintf 格式化没有小数位的浮点数
【发布时间】:2013-01-24 10:53:05
【问题描述】:

最初我使用带有浮点数的 sprintf 始终使用以下代码,小数点后 2 位:

static void MyFunc(char* buffer, const float percentage)
{
    sprintf(buffer, "%.2f", percentage);
}

传递的百分比值之一是 0x419FFFFF 20(调试器视图),这会将 20.00 打印到缓冲区中。

我想在不是整数时显示 2 个小数位,例如

94.74 displayed as 94.74
94.7  displayed as 94.70
0     displayed as 0
5     displayed as 5
100   displayed as 100

我目前正在使用以下代码:

static void MyFunc(char* buffer, const float percentage)
{
    int fractional_part = ((percentage - (int)percentage) * 100);
    if (0 == fractional_part)
    {
        sprintf(buffer, "%d", (int)percentage);
    }
    else
    {
        sprintf(buffer, "%.2f", percentage);
    }
}

现在如果通过 0x419FFFFF 20(调试器视图),小数部分计算为 99。我假设 fractional_part 的总和最终为 (19.99 - 19) * 100 = 99。为什么第一个示例不打印 19.99进入缓冲区?

我的问题的正确解决方案是什么?

【问题讨论】:

标签: c floating-point printf


【解决方案1】:

你的问题是近似的。

假设百分比为 19.999。那么fractional_part 将是 99,并且会调用浮点分支。

但是打印带有两位小数的 19.999 会将其四舍五入为 20.00,而 那个 就是打印的内容。

您始终可以使用浮点分支,以获得一致的结果,然后在 '.' 处截断。如果它以 '.00' 出现。否则,您的测试和printf 的内部结构有时会发生冲突。

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
        float percentage = 19.999;
        char buffer[50];

        for (percentage = 19.990; percentage < 20.001; percentage += 0.001)
        {
                sprintf(buffer, "%.2f", percentage);
                char *p = strstr(buffer, ".00");
                if (p) *p = 0x0;
                printf("%.3f rendered as %.2f and becomes %s\n", percentage, percentage, buffer);
        }
        return 0;
}

19.990 rendered as 19.99 and becomes 19.99
19.991 rendered as 19.99 and becomes 19.99
19.992 rendered as 19.99 and becomes 19.99
19.993 rendered as 19.99 and becomes 19.99
19.994 rendered as 19.99 and becomes 19.99
19.995 rendered as 19.99 and becomes 19.99
19.996 rendered as 20.00 and becomes 20
19.997 rendered as 20.00 and becomes 20
19.998 rendered as 20.00 and becomes 20
19.999 rendered as 20.00 and becomes 20
20.000 rendered as 20.00 and becomes 20
20.001 rendered as 20.00 and becomes 20

如果您不同意printf 的舍入策略,只需在percentage(的副本)上使用round() 并强制自己使用。或者您也可以,例如,sprintf() 使用三位数字,然后删除第三位。

在您的具体情况下(注意我的系统 (Linux x86_64) 如何呈现 0x419FFFFF):

#include <stdio.h>
#include <string.h>
#include <stdint.h>

int main(int argc, char **argv)
{
        float percentage = 3.1415;
        char buffer[50];

        ((uint32_t *)(&percentage))[0] = 0x419FFFFF;

        sprintf(buffer, "%.2f", percentage);
        char *p = strstr(buffer, ".00");
        if (p) *p = 0x0;
        printf("%.15f rendered as %.2f and becomes %s\n", percentage, percentage, buffer);
        return 0;
}


19.999998092651367 rendered as 20.00 and becomes 20

【讨论】:

    【解决方案2】:

    您可以尝试使用ceilf(f) == ffloorf(f) == f,而不是自己计算小数部分,如果f 是整数,则返回true。 另一种选择是使用 std lib 中的 modf (float x, float *ipart) 或 math 中的 fmod

    【讨论】:

    • 这样做我的调试器给出百分比 = 0x419FFFFF (20), ceilf(percentage) = 0x41A00000 (20), floorf(percentage) = 0x41980000 (19)。这些都不等于原始百分比。
    • 这些都不等于原始百分比,所以我的代码执行浮动路径并打印 20.00%,这正是我想要避免的。
    猜你喜欢
    • 1970-01-01
    • 2011-07-08
    • 1970-01-01
    • 2011-12-17
    • 2011-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-09
    相关资源
    最近更新 更多