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

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

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

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

  1. int main(void)
  2. {
  3. 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);
  4. fprint("I can print this too: %%, without a problem\n");
  5. return (0);
  6. }

字符串

我常用的printf()类函数:

  1. void fprint(char *str, ...)
  2. {
  3. va_list args;
  4. int length = strlen(str);
  5. char pr = '%';
  6. char *s_holder, c_holder;
  7. va_start(args, str);
  8. for (int i = 0 ; i < length ; i++)
  9. if (str[i] != '%')
  10. {
  11. write(1, &str[i], 1);
  12. }
  13. else if (str[i] == '%')
  14. {
  15. switch (str[i+1]){
  16. case 'd':
  17. print_integer(va_arg(args, int));
  18. i += 1;
  19. break;
  20. case 'c':
  21. c_holder = va_arg(args, int);
  22. write(1, &c_holder, 1);
  23. i += 1;
  24. break;
  25. case 's':
  26. s_holder = va_arg(args, char *);
  27. write(1, s_holder, strlen(s_holder));
  28. i += 1;
  29. break;
  30. case 'f':
  31. print_double(va_arg(args, double));
  32. i += 1;
  33. break;
  34. case '.':
  35. if (str[i + 2] >= '0' && str[i + 2] <= '9')
  36. {
  37. print_double2(va_arg(args, double), str[i + 2]);
  38. i += 3;
  39. }
  40. else
  41. {
  42. write(1, &str[i], 1);
  43. }
  44. break;
  45. default:
  46. write(1, &str[i], 1);
  47. break;
  48. }
  49. }
  50. }

处理浮点数的函数:

  1. void print_double2(int num, char cprecision)
  2. {
  3. int precision = atoi(cprecision);
  4. //handling the negative numbers
  5. if (num < 0)
  6. {
  7. write(1, "-", 1);
  8. num = -num;
  9. }
  10. //separating the integer part from the fractional part
  11. int int_part = (int)num;
  12. double fract_part = num - int_part;
  13. int int_fract;
  14. char digit;
  15. //printing the integer part of the double number with the print_integer function
  16. print_integer(int_part);
  17. write(1, ".", 1);
  18. //printing the fraction part
  19. while (fract_part > 0 && precision > 0)
  20. {
  21. fract_part *= 10;
  22. int_fract = (int)fract_part; //getting the int part after mult by 10
  23. digit = '0' + (int_fract % 10);
  24. write(1, &digit, 1); //printing the integer part
  25. fract_part -= int_fract; //removing the integer part to start the process again
  26. precision--; //decrementing the precision until it get to 0.
  27. }
  28. }


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

wn9m85ua

wn9m85ua1#

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

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

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


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

  1. struct state state = {0};
  2. for (..) {
  3. char c = str[....];
  4. switch (c) {
  5. case '.':
  6. if (state.seen_dot) {
  7. /* already seen a dot - error? ignore? up to you */
  8. }
  9. state.seen_dot = true;
  10. break;
  11. case 0: case 1: case 2: etc..
  12. if (state.seen_dot) {
  13. state.seen_precision = true;
  14. state.precision *= 10;
  15. state.precision += c - '0';
  16. } else {
  17. /* handle field width */
  18. }
  19. break;
  20. case 'f':
  21. print_double(state, va_arg(double));
  22. break;
  23. }
  24. }


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

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


实现你自己的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

  1. void print_double2(int num, char cprecision) {
  2. 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”。

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


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

展开查看全部

相关问题