输出浮点数而不以零结尾的 sprintf() 模式是什么?

发布于 2024-08-20 12:55:51 字数 100 浏览 13 评论 0原文

我想输出没有结尾零的浮点数。

示例:float 3.570000 应输出为 3.57

,float 3.00000 应输出为 3.0(所以这里是例外!)

I want to output my floats without the ending zeros.

Example: float 3.570000 should be outputted as 3.57

and float 3.00000 should be outputted as 3.0 (so here would be the exception!)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

请持续率性 2024-08-27 12:55:51

paxdiablo 的 morphNumericString() 的更高效且(在我看来)更清晰的形式。抱歉没有编译或测试。

void morphNumericString( char *s )
{
    char *p, *end, *decimal, *nonzero;

    // Find the last decimal point and non zero character
    end = p = strchr(s,'\0');
    decimal = nonzero = NULL;
    while( p > s )
    {
        p--;
        if( !nonzero && *p!='0' )
        {
            nonzero = p;
        }
        if( !decimal && *p=='.' )
        {
            decimal = p;
            break;  // nonzero must also be non NULL, so stop early
        }
    }

    // eg "4.3000" -> "4.3"
    if( decimal && nonzero && nonzero>decimal )
        *(nonzero+1) = '\0';

    // eg if(decimal)  "4.0000" -> "4.0"
    //    if(!decimal) "4" -> "4.0"
    else
        strcpy( decimal?decimal:end, ".0" );
}

A more efficient and (in my opinion) clearer form of paxdiablo's morphNumericString(). Sorry not compiled or tested.

void morphNumericString( char *s )
{
    char *p, *end, *decimal, *nonzero;

    // Find the last decimal point and non zero character
    end = p = strchr(s,'\0');
    decimal = nonzero = NULL;
    while( p > s )
    {
        p--;
        if( !nonzero && *p!='0' )
        {
            nonzero = p;
        }
        if( !decimal && *p=='.' )
        {
            decimal = p;
            break;  // nonzero must also be non NULL, so stop early
        }
    }

    // eg "4.3000" -> "4.3"
    if( decimal && nonzero && nonzero>decimal )
        *(nonzero+1) = '\0';

    // eg if(decimal)  "4.0000" -> "4.0"
    //    if(!decimal) "4" -> "4.0"
    else
        strcpy( decimal?decimal:end, ".0" );
}
情痴 2024-08-27 12:55:51

这对于标准 printf 语义来说是不可能的。在过去,我必须通过输出到一个字符串(类似于 "%.20f")然后对字符串进行后处理来完成此操作。

您可能正在寻找这样的东西:

#include <stdio.h>

void morphNumericString (char *s) {
    char *p;
    int count;

    // Find decimal point, if any.
    p = strchr (s,'.');

    if (p == NULL) {
        // No decimal, just add one fractional position.

        strcat (s, ".0");
    } else {
        // Decimal, start stripping off trailing zeros.

        while (s[strlen(s)-1] == '0') {
            s[strlen(s)-1] = '\0';
        }

        // If all fractional positions were zero, add one.

        if (s[strlen(s)-1] == '.') {
            strcat (s, "0");
        }
    }
}

 

int main (int argc, char *argv[]) {
    char str[100];
    int i;

    for (i = 1; i < argc; i++) {
        strcpy (str, argv[i]);
        morphNumericString (str);
        printf ("[%s] -> [%s]\n", argv[i], str);
    }

    return 0;
}

代码遍历每个参数,依次改变每个参数。以下记录显示了它的工作原理:

pax> ./qq 3.750000 12 12.507 47.90 56.0000000 76.0 0

[3.750000] -> [3.75]
[12] -> [12.0]
[12.507] -> [12.507]
[47.90] -> [47.9]
[56.0000000] -> [56.0]
[76.0] -> [76.0]
[0] -> [0.0]

但是您应该注意,如果使用浮点数或双精度数,则必须注意正常的浮点误差。在我的系统上3.57实际上是3.5699999999999998401,它根本不会被截断。

当然,您可以通过在 sprintf 中使用不太具体的输出位数(低于浮点的实际精度)来解决该问题。例如,我的系统上的 "%.10f" 输出 3.5700000000,被截断。将以下行添加到 main:

sprintf (str, "%.10f", 3.57);
morphNumericString (str);
printf ("[%.10f] -> [%s]\n", 3.57, str);

sprintf (str, "%.10f", 3.0);
morphNumericString (str);
printf ("[%.10f] -> [%s]\n", 3.0, str);

将导致以下添加的输出:

[3.5700000000] -> [3.57]
[3.0000000000] -> [3.0]

根据您的测试数据。

另一种可能性(如果您的输入范围和精度可以控制)是使用 g 格式说明符。它以 f 格式输出,其中精度是最大位数(而不是像 f 这样的固定数字)或指数格式(f) >e)。

基本上,只要显示所有信息,它就更喜欢非指数输出格式。仅当能够提供更多信息时,它才会切换为指数。一个简单的示例是带有 3.7.0000000004 的格式字符串 "%.4g"。前者将打印为 3.7,后者将打印为 4e-10


更新:对于那些更关心性能而不是鲁棒性或可读性的人,您可以尝试以下方法(我认为不必要,但对每个人来说

void morphNumericString (char *s) {
    char *p = strchr (s,'.');
    if (p == NULL) {
        strcat (s, ".0");
        return;
    }

    p = &(p[strlen(p)-1]);
    while ((p != s) && (*p == '0') && (*(p-1) != '.'))
        *p-- = '\0';
}

都是如此):它可能更快,但是,考虑到我所见过的现代编译器所做的极端优化,你永远不会太确定。我倾向于首先考虑可读性,只有在速度成为问题时才担心速度(YAGNI 可以同样适用于性能和功能)。

This is not possible with standard printf semantics. In, the past, I've had to do this by outputting to a string (with something like "%.20f") then post-processing the string.

Something like this is probably what you're looking for:

#include <stdio.h>

void morphNumericString (char *s) {
    char *p;
    int count;

    // Find decimal point, if any.
    p = strchr (s,'.');

    if (p == NULL) {
        // No decimal, just add one fractional position.

        strcat (s, ".0");
    } else {
        // Decimal, start stripping off trailing zeros.

        while (s[strlen(s)-1] == '0') {
            s[strlen(s)-1] = '\0';
        }

        // If all fractional positions were zero, add one.

        if (s[strlen(s)-1] == '.') {
            strcat (s, "0");
        }
    }
}

 

int main (int argc, char *argv[]) {
    char str[100];
    int i;

    for (i = 1; i < argc; i++) {
        strcpy (str, argv[i]);
        morphNumericString (str);
        printf ("[%s] -> [%s]\n", argv[i], str);
    }

    return 0;
}

The code runs through each of it's arguments, morphing each one in turn. The following transcript shows how it works:

pax> ./qq 3.750000 12 12.507 47.90 56.0000000 76.0 0

[3.750000] -> [3.75]
[12] -> [12.0]
[12.507] -> [12.507]
[47.90] -> [47.9]
[56.0000000] -> [56.0]
[76.0] -> [76.0]
[0] -> [0.0]

You should be aware however that, if using floats or doubles, you'll have to watch out for the normal floating point inaccuracies. On my system 3.57 is actually 3.5699999999999998401, which won't be truncated at all.

Of course, you can get around that problem by using a less-specific number of output digits in the sprintf, something less than the actual accuracy of the floating point point. For example, "%.10f" on my system outputs 3.5700000000 which will be truncated. Adding the following lines to main:

sprintf (str, "%.10f", 3.57);
morphNumericString (str);
printf ("[%.10f] -> [%s]\n", 3.57, str);

sprintf (str, "%.10f", 3.0);
morphNumericString (str);
printf ("[%.10f] -> [%s]\n", 3.0, str);

will result in the following added output:

[3.5700000000] -> [3.57]
[3.0000000000] -> [3.0]

as per your test data.

One other possibility (if your input range and precision can be controlled) is to use the g format specifier. This outputs in either f format where the precision is the maximum number of digits (rather than fixed number like f) or exponential format (e).

Basically, it prefers the non-exponential output format as long as all the information is shown. It will switch to exponential only if that will deliver more information. A simple example is the format string "%.4g" with 3.7 and .0000000004. The former would be printed as 3.7, the latter as 4e-10.


Update: for those more concerned about performance than robustness or readability, you could try the following (unnecessary in my opinion but to each their own):

void morphNumericString (char *s) {
    char *p = strchr (s,'.');
    if (p == NULL) {
        strcat (s, ".0");
        return;
    }

    p = &(p[strlen(p)-1]);
    while ((p != s) && (*p == '0') && (*(p-1) != '.'))
        *p-- = '\0';
}

It may be faster but, given the extreme optimisations I've seen modern compilers do, you can never be too sure. I tend to code for readability first and only worry about speed when it becomes an issue (YAGNI can apply equally to performance as well as functionality).

半步萧音过轻尘 2024-08-27 12:55:51

我的方法是:

  1. 实现一个函数trim(char c)来消除尾部'c',例如:

    void Trim(std::string &str, char c) {
    size_t len = str.length();

    const char *s = str.c_str();
    const char *p = s + len - 1;
    while (p != s && *p == c) {
        ——p;
    }
    ++ p;
    size_t 结束 = p - s;
    
    str = str.substr(0, 结束);
    

    }

  2. char buf[32];
    printf(buf, "%f", 0.01);
    std::string s(buf);
    修剪(buf,'0');

My approach is:

  1. implement a function trim(char c) to eliminate the trail 'c', for examaple:

    void trim(std::string &str, char c) {
    size_t len = str.length();

    const char *s = str.c_str();
    const char *p = s + len - 1;
    while (p != s && *p == c) {
        -- p;
    }
    ++ p;
    size_t end = p - s;
    
    str = str.substr(0, end);
    

    }

  2. char buf[32];
    printf(buf, "%f", 0.01);
    std::string s(buf);
    trim(buf, '0');

丢了幸福的猪 2024-08-27 12:55:51
void tidyFloatRepresentation(char *s)
{
    size_t end = strlen (s);
    size_t i = end - 1;
    char *lastZero = NULL;

    while (i && s[i] == '0') lastZero = &s[i--];
    while (i && s[i] != '.') i--;

    if (lastZero && s[i] == '.')
    {
        if (lastZero == &s[i] + 1)
            *(lastZero + 1) = '\0';
        else
            *lastZero = '\0';
    }
    else
    {
        strcpy (&s[end + 1], ".0");
    }
}

当输入以小数点结尾时,这将失败,但在大多数情况下,它会适当截断(或追加)。

void tidyFloatRepresentation(char *s)
{
    size_t end = strlen (s);
    size_t i = end - 1;
    char *lastZero = NULL;

    while (i && s[i] == '0') lastZero = &s[i--];
    while (i && s[i] != '.') i--;

    if (lastZero && s[i] == '.')
    {
        if (lastZero == &s[i] + 1)
            *(lastZero + 1) = '\0';
        else
            *lastZero = '\0';
    }
    else
    {
        strcpy (&s[end + 1], ".0");
    }
}

This will fail when the input ends with a decimal point, but for the most part, it will truncate (or append) appropriately.

菊凝晚露 2024-08-27 12:55:51

这是另一个字符串修改函数。我确实测试过。

它比 Bill 和 Diablo 的长,但它可以处理尾随 9 和 0,并且应该表现良好。

在点后留下一个尾随零。您必须使用 sprintf 进行截断才能获得正确的尾随 9。

void morphNumericString( char *s ) {
    char *point = s;
    while ( * point && * point != '.' ) ++ point;
    char *last = strchr( point, 0 );
    if ( point == last ) {
        * point = '.';
        ++ last;
    }
    if ( point == last - 1 ) {
        * last = '0';

    } else {
        -- last;
        if ( * last == '0' ) {
            while ( * last == '0' ) -- last;

        } else if ( * last == '9' ) {
            while ( * last == '9' || * last == '.' ) {
                if ( * last == '9' ) * last = '0';
                -- last;
            }
            ( * last ) ++;
        }
        if ( last < point + 1 ) last = point + 1;
    }
    * ++ last = 0;
}

编辑: 哎呀,输入 999.999 时会失败。留给读者作为练习;v)

Here's another string modification function. I did test it.

It's longer than Bill's and Diablo's, but it handles trailing 9's as well as 0's and should perform well.

Leaves a single trailing zero after the dot. You'll have to truncate using sprintf to get proper trailing 9's.

void morphNumericString( char *s ) {
    char *point = s;
    while ( * point && * point != '.' ) ++ point;
    char *last = strchr( point, 0 );
    if ( point == last ) {
        * point = '.';
        ++ last;
    }
    if ( point == last - 1 ) {
        * last = '0';

    } else {
        -- last;
        if ( * last == '0' ) {
            while ( * last == '0' ) -- last;

        } else if ( * last == '9' ) {
            while ( * last == '9' || * last == '.' ) {
                if ( * last == '9' ) * last = '0';
                -- last;
            }
            ( * last ) ++;
        }
        if ( last < point + 1 ) last = point + 1;
    }
    * ++ last = 0;
}

Edit: Yikes, this fails on input like 999.999. Left as an exercise to the reader ;v)

丢了幸福的猪 2024-08-27 12:55:51

直接计算小数部分可能会更容易:

double value= -3.57;

double int_part;
double frac= modf(value, &int_part);

int64 int_part_as_int= int_part;
int significant_digits= 10;
int64 fractional_scale= pow(10., significant_digits);
int64 fraction_magnitude= fabs(frac)*fractional_scale + 0.5;

fractional_magnitude/fractional_scale 将是四舍五入到 significant_digits sig Figs 的分数。即使使用双打,也保证不会溢出。

格式化分数应该很简单。

It might be easier to calculate the fractional part directly:

double value= -3.57;

double int_part;
double frac= modf(value, &int_part);

int64 int_part_as_int= int_part;
int significant_digits= 10;
int64 fractional_scale= pow(10., significant_digits);
int64 fraction_magnitude= fabs(frac)*fractional_scale + 0.5;

fractional_magnitude/fractional_scale will be the fraction rounded to significant_digits sig figs. Even with doubles, this is guaranteed not to overflow.

Formatting the fraction should be straightforward.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文