32位IEEE754浮点数转十进制程序报错:输出-nan求助
问题描述
实现将32位IEEE754浮点数的命令行参数转换为十进制浮点数的C程序时,测试输入./f32 00111111100000000000000000000000,程序输出-nan,无法得到正确结果(预期输出应为1.0000)。
原代码如下:
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> int main(int argc, char *argv[]) { (void)argc; // variables long signBit; long expVal = 0; double mantissa = 0.0; int expCounter = 0; int fracCounter = -1; char numString[33]; char expBits[9]; char fracBits[24]; int len = strlen(argv[1]); for (int i = 1; i < len; i++) { numString[i - 1] = argv[1][i]; } // sets signBit equal to first index from cl argument signBit = strtol(&argv[1][0], NULL, 10); strncpy(expBits, numString + 1, 7); strncpy(fracBits, numString + 9, 23); for (int i = 0; i < 7; i++) { long bitVal = strtol(&expBits[i - 1], NULL, 10); // calculates e expVal += (bitVal * (long)pow(2, expCounter)); expCounter++; } expVal -= 127; // adjusts expVal for bias for (int i = 0; i < 31; i++) { int fracVal = (fracBits[i] == '1') ? 1 : 0; // calculates mantissa mantissa += (fracVal * pow(2, fracCounter)); fracCounter--; } // convert to float in base 10 double float_value = (pow(-1, signBit)) * (1 + mantissa) * (pow(2, expVal)); printf("%.4f\n", float_value); printf("\n"); return EXIT_SUCCESS; }
作业要求
In f32.c, write a program that takes a 32-bit IEEE754 representation
of a float as a command-line argument and outputs the float it
represents in base-10.To account for floating-point error, when printing the float, use
printf("%.4f\n", float_value) to print to 4 decimal places.These libraries should be useful:
string.hargv[1]is a string. You are allowed to usestring.hYou do not have to convert the exponent bits to a base-10 integer
yourself. Please usestrtolto convert the exponent bits (const char*) tolong. PassNULLas the second argument. You may usestrlento get the length of a string. You may usestrncpyto
extract the exponent bits and mantissa bits from the 32-bit string. It
can be used similarly to slicing in Python (str[start:end]) or
Java'ssubstring(begIndex, endIndex)method.
math.hPlease do not implement exponentiation yourself. For
exponentiation, usepow
错误排查
strtol进制参数错误:原代码中用strtol转换指数位时使用了十进制(base=10),但指数位是二进制字符串,应该指定base=2,这会导致指数值计算完全错误,是输出NaN的核心原因。- 字符串提取错误:
- 指数位应该提取8位(IEEE754单精度的指数位长度),原代码只复制了7位,且未手动添加字符串终止符
\0,导致strtol读取越界。 - 尾数位提取的起始位置错误,且循环跑了31次(远超23位的长度),访问数组越界内存,得到随机值。
- 指数位应该提取8位(IEEE754单精度的指数位长度),原代码只复制了7位,且未手动添加字符串终止符
- 数组越界访问:计算指数位的循环中,
i=0时&expBits[i-1]指向expBits[-1],属于非法内存访问,获取到无效值。 - 冗余的
numString数组:原代码复制原字符串到numString但未添加终止符,后续操作容易引发未定义行为,且完全没必要,直接操作原字符串即可。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> int main(int argc, char *argv[]) { // 参数合法性检查:确保输入是32位字符串 if (argc != 2 || strlen(argv[1]) != 32) { fprintf(stderr, "Usage: %s <32-bit IEEE754 string>\n", argv[0]); return EXIT_FAILURE; } // 提取符号位:第1位(索引0) long signBit = strtol(&argv[1][0], NULL, 10); // 提取指数位:第2-9位(索引1-8),共8位,手动添加终止符 char expBits[9]; strncpy(expBits, &argv[1][1], 8); expBits[8] = '\0'; // 二进制转十进制,再减去偏移量127 long expVal = strtol(expBits, NULL, 2); expVal -= 127; // 提取尾数位:第10-32位(索引9-31),共23位,手动添加终止符 char fracBits[24]; strncpy(fracBits, &argv[1][9], 23); fracBits[23] = '\0'; // 计算尾数值 double mantissa = 0.0; for (int i = 0; i < 23; i++) { if (fracBits[i] == '1') { mantissa += pow(2, -(i + 1)); } } // 计算最终浮点数 double float_value = pow(-1, signBit) * (1 + mantissa) * pow(2, expVal); printf("%.4f\n", float_value); return EXIT_SUCCESS; }
关键修改说明
- 添加参数合法性检查,避免非法输入。
- 修正
strtol的进制参数为2,正确转换二进制指数位。 - 正确提取指数位和尾数位,手动添加字符串终止符,避免越界读取。
- 尾数位循环限制为23次,只计算有效位,避免内存越界。
- 移除冗余的
numString数组,直接操作原命令行参数字符串,减少错误点。
内容的提问来源于stack exchange,提问作者papayaaa

