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

时间:2010-02-09 00:39:40

标签: c++ pattern-matching printf

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

示例:float 3.570000应输出为3.57

并且浮动3.00000应该输出为3.0(所以这里将是例外!)

6 个答案:

答案 0 :(得分:2)

使用标准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)或指数格式(e)。< / p>

基本上,只要显示所有信息,它就更喜欢非指数输出格式。只有当它能提供更多信息时,它才会切换到指数。一个简单的示例是带有"%.4g"3.7的格式字符串.0000000004。前者将打印为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可以同样适用于性能和功能)。

答案 1 :(得分:2)

更有效且(在我看来)更清晰的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" );
}

答案 2 :(得分: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");
    }
}

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

答案 3 :(得分:0)

我的方法是:

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

    void trim(std :: string&amp; 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); 修剪(buf,'0');

答案 4 :(得分:0)

这是另一个字符串修改函数。我做了测试。

它比比尔和暗黑破坏神的时间更长,但它处理的是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)

答案 5 :(得分:0)

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

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。即使有双打,也保证不会溢出。

格式化分数应该很简单。