如何精确输出与输入一致的浮点数?(C语言项目问题)
解决精确输出输入一致浮点数的方案
核心问题分析
直接用double存储浮点数后再反向解析输出会出现精度误差——因为二进制浮点数无法精确表示所有十进制小数(比如0.1),存储后的值是近似值,拆分输出时会多出尾数位。必须保留输入的原始字符串形式,同时将字符串手动转换为double用于计算,输出时直接打印原始字符串才能保证和输入完全一致。
实现步骤
1. 手动实现字符串转double函数(替代atof)
因为不能使用atof,我们自己解析字符串的整数和小数部分,转换为double类型:
double str_to_double(const char *str) { double result = 0.0; double decimal_part = 0.0; int sign = 1; int i = 0; double divisor = 10.0; // 处理正负号 if (str[i] == '-') { sign = -1; i++; } else if (str[i] == '+') { i++; } // 处理整数部分 while (str[i] != '\0' && str[i] != '.') { result = result * 10 + (str[i] - '0'); i++; } // 处理小数部分 if (str[i] == '.') { i++; while (str[i] != '\0') { decimal_part += (str[i] - '0') / divisor; divisor *= 10; i++; } } return sign * (result + decimal_part); }
2. 读取输入:保存原始字符串+转换为double
针对你补充的lect_id+多分数的输入格式(比如A 1.11111 2.023 3.043),我们逐个字符读取,保存每个分数的原始字符串,同时转换为数值用于计算:
#include <stdio.h> #include <string.h> #define MAX_LECTURES 100 // 最大课程数 #define MAX_SCORES 50 // 每门课最大分数数量 #define MAX_STR_LEN 30 // 单个分数字符串最大长度 double str_to_double(const char *str) { // 上述函数实现 double result = 0.0; double decimal_part = 0.0; int sign = 1; int i = 0; double divisor = 10.0; if (str[i] == '-') { sign = -1; i++; } else if (str[i] == '+') { i++; } while (str[i] != '\0' && str[i] != '.') { result = result * 10 + (str[i] - '0'); i++; } if (str[i] == '.') { i++; while (str[i] != '\0') { decimal_part += (str[i] - '0') / divisor; divisor *= 10; i++; } } return sign * (result + decimal_part); } int main() { char lect_ids[MAX_LECTURES]; char score_strings[MAX_LECTURES][MAX_SCORES][MAX_STR_LEN]; double score_values[MAX_LECTURES][MAX_SCORES]; int score_counts[MAX_LECTURES] = {0}; int lecture_count = 0; char c; int str_idx = 0; int current_score_idx = 0; // 逐字符读取输入 while ((c = getchar()) != EOF && lecture_count < MAX_LECTURES) { // 跳过前导空格/换行 if ((c == ' ' || c == '\n') && str_idx == 0) { continue; } // 读取课程ID(每行第一个非空格字符) if (score_counts[lecture_count] == 0 && str_idx == 0) { lect_ids[lecture_count] = c; // 跳过ID后的空格 while ((c = getchar()) != EOF && c == ' '); ungetc(c, stdin); // 把第一个分数的字符放回输入流 str_idx = 0; continue; } // 遇到换行:结束当前课程的分数读取 if (c == '\n') { if (str_idx > 0) { score_strings[lecture_count][current_score_idx][str_idx] = '\0'; score_values[lecture_count][current_score_idx] = str_to_double(score_strings[lecture_count][current_score_idx]); score_counts[lecture_count]++; } lecture_count++; current_score_idx = 0; str_idx = 0; continue; } // 遇到空格:结束当前分数的读取 if (c == ' ') { if (str_idx > 0) { score_strings[lecture_count][current_score_idx][str_idx] = '\0'; score_values[lecture_count][current_score_idx] = str_to_double(score_strings[lecture_count][current_score_idx]); score_counts[lecture_count]++; current_score_idx++; str_idx = 0; } continue; } // 保存当前分数的字符 if (str_idx < MAX_STR_LEN - 1) { score_strings[lecture_count][current_score_idx][str_idx] = c; str_idx++; } } // 处理最后一门课的最后一个分数 if (str_idx > 0 && lecture_count < MAX_LECTURES) { score_strings[lecture_count][current_score_idx][str_idx] = '\0'; score_values[lecture_count][current_score_idx] = str_to_double(score_strings[lecture_count][current_score_idx]); score_counts[lecture_count]++; lecture_count++; } // 输出原始格式的分数 printf("输出结果:\n"); for (int i = 0; i < lecture_count; i++) { printf("%c ", lect_ids[i]); for (int j = 0; j < score_counts[i]; j++) { if (j > 0) printf(" "); printf("%s", score_strings[i][j]); } printf("\n"); } // 计算并输出平均分 printf("\n平均分:\n"); for (int i = 0; i < lecture_count; i++) { if (score_counts[i] == 0) { printf("%c: 无有效分数\n", lect_ids[i]); continue; } double sum = 0.0; for (int j = 0; j < score_counts[i]; j++) { sum += score_values[i][j]; } printf("%c: %.6f\n", lect_ids[i], sum / score_counts[i]); } return 0; }
为什么之前的方法失败
- 浮点数精度问题:直接用
double存储后拆分输出,因为二进制浮点数的近似性,会导致小数部分出现多余尾数位。 - 输入读取逻辑混乱:嵌套的
scanf会打乱输入流的读取指针,导致读取到错误的字符,引发段错误或读取结果异常。
内容的提问来源于stack exchange,提问作者acd123
相关产品推荐
相关产品推荐

