C语言 如何在自定义的类似printf()的函数中处理浮点精度,例如:%.2f?

kmynzznz  于 2023-08-03  发布在  其他
关注(0)|答案(2)|浏览(95)

我试图创建一个类似于printf()的自定义函数,但在精确打印浮点数时遇到错误。当我试图处理格式说明符的精度部分时,例如%.2f,就会出现问题。如何在自定义的类似printf()的函数中实现浮点数的精度处理?

**main函数:**有我想打印的文本:

int main(void)
{
    fprint("first I fixed the integer call u can see here I can print an integer %d, I also can print a character: %c and a string: %s, and lastly I can print a float value like: %.2f\n", 567, 'a', "Hey", 65.13);
    fprint("I can print this too: %%, without a problem\n");
    return (0);
}

字符串

我常用的printf()类函数:

void fprint(char *str, ...)
{
    va_list args;
    int length = strlen(str);
    char pr = '%';
    char *s_holder, c_holder;

    va_start(args, str);

    for (int i = 0 ; i < length ; i++)
        if (str[i] != '%')
    {
        write(1, &str[i], 1);
    }
    else if (str[i] == '%')
    {
        switch (str[i+1]){
            case 'd':
                print_integer(va_arg(args, int));
                i += 1;
                break;
            case 'c':
                c_holder = va_arg(args, int);
                write(1, &c_holder, 1);
                i += 1;
                break;
            case 's':
                s_holder = va_arg(args, char *);
                write(1, s_holder, strlen(s_holder));
                i += 1;
                break;
            case 'f':
                print_double(va_arg(args, double));
                i += 1;
                break;
            case '.':
                if (str[i + 2] >= '0' && str[i + 2] <= '9')
                {
                    print_double2(va_arg(args, double), str[i + 2]);
                    i += 3;
                }
                else
                {
                    write(1, &str[i], 1);
                }
                break;
            default:
                write(1, &str[i], 1);
                break;
        }
    }
}

处理浮点数的函数:

void print_double2(int num, char cprecision)
{
    int precision = atoi(cprecision);

    //handling the negative numbers
    if (num < 0)
    {
        write(1, "-", 1);
        num = -num;
    }

    //separating the integer part from the fractional part
    int int_part = (int)num;
    double fract_part = num - int_part;
    
    int int_fract;
    char digit;

    //printing the integer part of the double number with the print_integer function
    print_integer(int_part);

    write(1, ".", 1);

    //printing the fraction part
    while (fract_part > 0 && precision > 0)
    {
        fract_part *= 10;
        int_fract = (int)fract_part; //getting the int part after mult by 10
        digit = '0' + (int_fract % 10);
        write(1, &digit, 1); //printing the integer part
        fract_part -= int_fract; //removing the integer part to start the process again
        precision--; //decrementing the precision until it get to 0.
    }
}


我真的不知道该怎么办,我还是一个初学者,我一个月前才开始学习。我试着用chatgpt,但它没有帮助。😓

wn9m85ua

wn9m85ua1#

case '.':
            if (str[i + 2] >= '0' && str[i + 2] <= '9')
            {
                print_double2(va_arg(args, double), str[i + 2]);

字符串
您正在用点打印。你必须在'f'上打印,dot只指定 * 下一个数字 * 表示精度。我建议有状态,如果你看到一个点,并看到精度,你存储,然后将所有的状态传递给打印机。您可以在执行流中保留状态(当注意到一个点时,采取不同的路径),但不同状态组合的数量太大,将变得不可读。请考虑:

struct state {
    bool seen_dot;
    bool seen_precision;
    int precision;
    /* TODO: hash, zero, field width, h,l,L,z,t,j modifiers, hex or octal modifiers, etc. */
};


然后,当解析时,你在字符到来时填充状态,并根据当前解析状态采取不同的行动:

struct state state = {0};
for (..) {
   char c = str[....];
   switch (c) {
      case '.':
         if (state.seen_dot) {
              /* already seen a dot - error? ignore? up to you */
         }
         state.seen_dot = true;
         break;
      case 0: case 1: case 2: etc..
         if (state.seen_dot) {
             state.seen_precision = true;
             state.precision *= 10;
             state.precision += c - '0';
         } else {
             /* handle field width */
         }
         break;
      case 'f':
         print_double(state, va_arg(double));
         break;
     }
}


然后在打印时,您必须考虑所有选项:

void print_double(struct state state, double var) {
   // default precision is 6 for floating point numbers
   int precision = state.seen_precision ? state.precision : 6;
   // print with precision digits after comma
   // handle field width. Handle FfAaGgEe all differently
}


实现你自己的printf,尤其是浮点打印是一项非常艰巨的任务。特别是对于浮点数,你可能想阅读好的旧https://lists.nongnu.org/archive/html/gcl-devel/2012-10/pdfkieTlklRzN.pdf,学习标准的math.h头https://en.cppreference.com/w/c/numeric/math/modf和研究https://en.wikipedia.org/wiki/IEEE_754
此外,github上有无数的printf用户实现,你可以从中获得灵感。

5vf7fwbs

5vf7fwbs2#

我真的不知道该怎么办

保存时间,开启所有警告

后面的都是小事。启用所有警告。

类型不匹配

下面是错误的代码,因为atoi()需要一个指向 string 的指针,但代码提供了char

void print_double2(int num, char cprecision) {
int precision = atoi(cprecision);  // Bad

字符串

类型错误

print_double2(va_arg(args, double), str[i + 2]);发送一个doule作为第一个地址,但void print_double2(int num, char cprecision)需要一个int
double fract_part = num - int_part;总是0,因为numint。它没有分数。

溢出

如果numdouble,则int int_part = (int)num;存在溢出风险。研究modf()

弱方法

while (fract_part > 0 && precision > 0)尝试一次打印一位小数,但不尝试对结果进行 * 舍入 *。
我希望下面打印的是“4.88”而不是“4.87”。

fprint("%.2f", 4.875);


好的舍入是困难的。不要将数字分成整数和小数部分,因为接近xxx.999xxx的情况将失败。
更好的方法取决于您希望代码有多好-边缘情况很多。

相关问题