1 strtof() 函数
1.1 函数原型
#include <stdlib.h> // 必须包含这个头文件才能使用 strtof()
#include <errno.h> // 包含 errno 和 ERANGE
#include <float.h> // 包含 FlOAT_MAX 和 FLOAT_MIN
#include <math.h> // 包含 HUGE_VALF(inf)
float strtof(const char *nptr, char **endptr);
1.2 功能说明
strtof() 函数用于将字符串转换为单精度浮点数(float)。与 atof() 等函数相比,strtof() 提供了更灵活、安全的转换机制,支持完善的错误检查和转换结束位置定位。
- 参数:
- nptr:指向待转换的字符串(以空字符 \0 结尾)。
- endptr:用于存储转换结束位置的指针。如果不需要使用该信息,可以将其设置为 NULL。
- 返回值:
- 成功转换:返回对应的 float 值。
- 无效输入:若未执行任何转换,返回 0.0f。
- 溢出情况:若转换结果超出 float 的表示范围:
- 正溢出:返回 HUGE_VALF(表示正无穷大,inf),并设置 errno 为 ERANGE。
- 负溢出:返回 -HUGE_VALF(表示负无穷大,-inf),并设置 errno 为 ERANGE。
- 下溢情况:若转换结果绝对值过小(小于 FLT_MIN),返回 0.0f,并设置 errno 为 ERANGE(但某些实现可能不会设置 errno,具体行为取决于实现)。
错误处理与范围检查相关概念:
- errno
- 含义:errno 是一个全局变量(类型为 int,定义在 <errno.h> 头文件中),用于存储最近一次函数调用产生的错误代码。
- 作用:当 strtof() 等函数检测到错误(如数值溢出或下溢)时,会将 errno 设置为特定的错误码(如 ERANGE),以指示错误类型。
- 使用建议:在调用 strtof() 之前,应清除 errno(例如,通过 errno = 0),以便在转换后能够正确检测到是否发生了错误。
- ERANGE
- 含义:RANGE 是一个宏常量(通常值为 34,定义在 <errno.h> 头文件中),表示 “结果超出范围”(Range Error)。
- 使用场景:当 strtof() 检测到输入的字符串表示的数值超出 float 的表示范围(过大或过小)时,会将 errno 设置为 ERANGE,表明发生了溢出或下溢错误。
- HUGE_VALF
- 含义:HUGE_VALF 是一个宏常量(定义在 <math.h> 头文件中),表示正无穷大(inf)的 float 值。
- 使用场景:当 strtof() 发生正溢出时,返回 HUGE_VALF(inf)。
- FLT_MAX 和 FLT_MIN
- 含义:FLT_MAX 和 FLT_MIN 是宏定义(定义在 <float.h> 头文件中),分别表示 float 类型的最大值和最小正规范化值。
- 使用场景:
- 若输入字符串表示的数值超过 FLT_MAX,strtof() 会返回 HUGE_VALF(inf)并设置 errno 为 ERANGE。
- 若输入字符串表示的数值绝对值小于 FLT_MIN,strtof() 会返回 0.0f 并可能设置 errno 为 ERANGE(取决于实现)。
1.3 转换规则
- 忽略前导空格:在解析字符串时,自动跳过字符串开头处的所有空白字符,包括空格(' ')、制表符('\t')、换行符('\n')等。
- 识别正负号:支持在数字前添加可选的符号字符:
- '+' 表示最终数值为正数。
- '-' 表示最终数值为负数。
- 若未指定符号,默认数值为正数。
- 读取数字:解析字符串中的数字部分,支持以下格式:
- 整数部分(如 "123")。
- 小数部分(如 ".123" 或 "123.456")。
- 指数部分(如 "1.23e4" 或 "1.23E-4")。
- 停止转换:遇到第一个不符合浮点数规则的字符时停止解析,后续字符将被忽略。
- 返回结果:将解析的数字转换为 float 类型返回。
- 错误处理:
- 完全成功转换:
- endptr 指向字符串末尾的空字符 '\0',可通过 if (*endptr == '\0') 验证。
- 示例:"3.1415926", NULL → 返回 3.141593。
- 部分成功转换:
- endptr 指向第一个非数字字符,可通过 if (*endptr != '\0') 验证。
- 示例:"3.14abc", NULL → 返回 3.14,endptr 指向 'a'。
- 无效输入:
- endptr 指向原始字符串起始地址,可通过 if (endptr == str) 验证(str 为原始字符串指针)。
- 示例:"HelloWorld", NULL → 返回 0.0f,endptr 指向 'H'。
- 溢出处理:需结合 errno 和 endptr 进行判断。
- 正溢出:
- 返回 HUGE_VALF(表示正无穷大,inf),并设置 errno 为 ERANGE。
- 示例:"1e50", NULL → 返回 HUGE_VALF。
- 负溢出:
- 返回 -HUGE_VALF(表示负无穷大,-inf),并设置 errno 为 ERANGE。
- 示例:"-1e50", NULL → 返回 -HUGE_VALF。
- 下溢:
- 返回 0.0f,并可能设置 errno 为 ERANGE(具体行为取决于实现)。
- 示例:"1e-50", NULL → 返回 0.0f。
- 正溢出:
- 完全成功转换:
转换示例:
输入字符串 | endptr 参数 | 返回值 | 说明 |
---|---|---|---|
"3.1415926" | NULL | 3.141593f | 完全成功转换 |
"-2.71828" | NULL | -2.718280f | 完全成功转换(负数) |
" +123.456" | NULL | 123.456000f | 忽略前导空格 |
"3.14abc" | NULL | 3.140000f | 部分成功转换,endptr 指向 'a' |
"HelloWorld" | NULL | 0.000000f | 无效输入,endptr 指向 'H' |
"1.23e4" | NULL | 12300.000000f | 科学计数法转换 |
"1e50" | NULL | HUGE_VALF(inf) | 正溢出 |
"-1e50" | NULL | -HUGE_VALF(-inf) | 负溢出 |
"1e-50" | NULL | 0.000000f | 下溢 |
1.4 注意事项
- 错误检测:
- 方法:通过检查 endptr 是否等于原始字符串的起始地址(str)来判断转换是否完全失败。
- 细节:
- 若 endptr == str:表示未解析出任何有效数字(如输入为 "HelloWorld")。
- 若 *endptr != '\0':表示部分成功转换如输入为 "123.45abc",endptr 指向 'a')。
- 若 *endptr == '\0':表示完全成功转换(如输入为 "123.45")。
- 溢出处理:
- 条件:当输入的数值超出 float 的表示范围时,strtof() 会设置 errno 为 ERANGE。
- 行为:
- 正溢出:返回 HUGE_VALF(inf)。
- 负溢出:返回 -HUGE_VALF(-inf)。
- 建议:在调用 strtof() 前,应清除 errno(如 errno = 0),以便后续检测。
- 科学计数法支持:strtof() 支持科学计数法(如 "1.23e4" 或 "1.23E-4"),这是 atof() 等函数不具备的特性。
- 推荐使用:相比 atof(),strtof() 更安全,支持错误检查和更灵活的浮点数格式解析。
1.5 示例程序
#include <stdio.h>
#include <stdlib.h> // 必须包含这个头文件才能使用 strtof()
#include <errno.h> // 包含 errno 和 ERANGE
#include <float.h> // 包含 FlOAT_MAX 和 FLOAT_MIN
#include <math.h> // 包含 HUGE_VALF
int main()
{
char *endptr; // 用于存储转换结束的位置
float result; // 用于存储转换结果
// 示例 1:基本转换
result = strtof("3.1415926", &endptr);
printf("转换结果1: %f, 结束位置: %s\n", result, endptr); // 输出: 转换结果1: 3.141593, 结束位置: (null)
// 示例 2:带符号的数字
result = strtof("-2.71828", &endptr);
printf("转换结果2: %f, 结束位置: %s\n", result, endptr); // 输出: 转换结果2: -2.718280, 结束位置: (null)
// 示例 3:带前导空格和符号
result = strtof(" +123.456", &endptr);
printf("转换结果3: %f, 结束位置: %s\n", result, endptr); // 输出: 转换结果3: 123.456000, 结束位置: (null)
// 示例 4:部分有效的数字
result = strtof("3.14abc", &endptr);
printf("转换结果4: %f, 结束位置: %s\n", result, endptr); // 输出: 转换结果4: 3.140000, 结束位置: abc
// 示例 5:无效输入
result = strtof("HelloWorld", &endptr);
printf("转换结果5: %f, 结束位置: %s\n", result, endptr); // 输出: 转换结果5: 0.000000, 结束位置: HelloWorld
// 示例 6:科学计数法
result = strtof("1.23e4", &endptr);
printf("转换结果6: %f, 结束位置: %s\n", result, endptr); // 输出: 转换结果6: 12300.000000, 结束位置: (null)
// 示例 7:正溢出检测
errno = 0; // 重置 errno, 清除之前的错误
result = strtof("1e50", &endptr);
if (errno == ERANGE)
{
printf(" errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
printf(" HUGE_VALF: %f\n", HUGE_VALF); // 输出: HUGE_VALF: inf
printf(" FLOAT_MAX: %e, FLOAT_MIN: %e\n", FLT_MAX, FLT_MIN); // 输出: FLOAT_MAX: 3.402823e+38, FLOAT_MIN: 1.175494e-38
printf("转换结果7: 超出范围,返回 HUGE_VALF(inf): %f\n", result); // 输出: 转换结果7: 超出范围,返回 HUGE_VALF(inf): inf
}
else
{
printf("转换结果7: %f\n", result);
}
// 示例 8:负溢出检测
errno = 0; // 重置 errno, 清除之前的错误
result = strtof("-1e50", &endptr);
if (errno == ERANGE)
{
printf(" errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
printf(" -HUGE_VALF: %f\n", -HUGE_VALF); // 输出: -HUGE_VALF: -inf
printf(" FLOAT_MAX: %e, FLOAT_MIN: %e\n", FLT_MAX, FLT_MIN); // 输出: FLOAT_MAX: 3.402823e+38, FLOAT_MIN: 1.175494e-38
printf("转换结果8: 超出范围,返回 -HUGE_VALF(-inf): %f\n", result); // 输出: 转换结果8: 超出范围,返回 -HUGE_VALF(-inf): -inf
}
else
{
printf("转换结果8: %f\n", result);
}
// 示例 9:下溢检测
errno = 0; // 重置 errno, 清除之前的错误
result = strtof("1e-50", &endptr);
if (errno == ERANGE)
{
printf(" errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
printf(" HUGE_VALF: %f, -HUGE_VALF: %f\n", HUGE_VALF, -HUGE_VALF); // 输出: HUGE_VALF: inf, -HUGE_VALF: -inf
printf(" FLOAT_MAX: %e, FLOAT_MIN: %e\n", FLT_MAX, FLT_MIN); // 输出: FLOAT_MAX: 3.402823e+38, FLOAT_MIN: 1.175494e-38
printf("转换结果9: 字符串表示的数值绝对值小于 FLT_MIN,返回 0.0f: %f\n", result); // 输出: 转换结果9: 字符串表示的数值绝对值小于 FLT_MIN,返回 0.0f: 0.000000
}
else
{
printf("转换结果9: %f\n", result);
}
// 示例 10:检查转换是否完全成功
result = strtof("123.456abc", &endptr);
if (*endptr != '\0')
{
printf("转换结果10: %f, 但字符串未完全转换\n", result); // 输出: 转换结果10: 123.456000, 但字符串未完全转换
}
else
{
printf("转换结果10: %f\n", result);
}
return 0;
}
程序在 VS Code 中的运行结果如下所示:
2 strtod() 函数
2.1 函数原型
#include <stdlib.h> // 必须包含这个头文件才能使用 strtod()
#include <errno.h> // 包含 errno 和 ERANGE
#include <float.h> // 包含 DBL_MAX 和 DBL_MIN
#include <math.h> // 包含 HUGE_VAL
double strtod(const char *nptr, char **endptr);
2.2 功能说明
strtod() 函数用于将字符串转换为双精度浮点数(double)。与 atof() 等函数相比,strtod() 提供了更灵活、安全的转换机制,支持完善的错误检查和转换结束位置定位。
- 参数:
- nptr:指向待转换的字符串(以空字符 \0 结尾)。
- endptr:用于存储转换结束位置的指针。如果不需要使用该信息,可以将其设置为 NULL。
- 返回值:
- 成功转换:返回对应的 double 值。
- 无效输入:若未执行任何转换,返回 0.0。
- 溢出情况:若转换结果超出 double 的表示范围:
- 正溢出:返回 HUGE_VAL(表示正无穷大,inf),并设置 errno 为 ERANGE。
- 负溢出:返回 -HUGE_VAL(表示负无穷大,-inf),并设置 errno 为 ERANGE。
- 下溢情况:若转换结果绝对值过小(小于 DBL_MIN),返回 0.0,并设置 errno 为 ERANGE(但某些实现可能不会设置 errno,具体行为取决于实现)。
错误处理与范围检查相关概念:
- errno
- 含义:errno 是一个全局变量(类型为 int,定义在 <errno.h> 头文件中),用于存储最近一次函数调用产生的错误代码。
- 作用:当 strtod() 等函数检测到错误(如数值溢出或下溢)时,会将 errno 设置为特定的错误码(如 ERANGE),以指示错误类型。
- 使用建议:在调用 strtod() 之前,应清除 errno(例如,通过 errno = 0),以便在转换后能够正确检测到是否发生了错误。
- ERANGE
- 含义:RANGE 是一个宏常量(通常值为 34,定义在 <errno.h> 头文件中),表示 “结果超出范围”(Range Error)。
- 使用场景:当 strtod() 检测到输入的字符串表示的数值超出 double 的表示范围(过大或过小)时,会将 errno 设置为 ERANGE,表明发生了溢出或下溢错误。
- HUGE_VAL
- 含义:HUGE_VAL 是一个宏常量(定义在 <math.h> 头文件中),表示正无穷大(inf)的 double 值。
- 使用场景:当 strtod() 发生正溢出时,返回 HUGE_VAL(inf)。
- DBL_MAX 和 DBL_MIN
- 含义:DBL_MAX 和 DBL_MIN 是宏定义(定义在 <float.h> 头文件中),分别表示 double 类型的最大值和最小正规范化值。
- 使用场景:
- 若输入字符串表示的数值超过 DBL_MAX,strtod() 会返回 HUGE_VAL(inf)并设置 errno 为 ERANGE。
- 若输入字符串表示的数值绝对值小于 DBL_MIN,strtod() 会返回 0.0 并可能设置 errno 为 ERANGE(取决于实现)。
2.3 转换规则
- 忽略前导空格:在解析字符串时,自动跳过字符串开头处的所有空白字符,包括空格(' ')、制表符('\t')、换行符('\n')等。
- 识别正负号:支持在数字前添加可选的符号字符:
- '+' 表示最终数值为正数。
- '-' 表示最终数值为负数。
- 若未指定符号,默认数值为正数。
- 读取数字:解析字符串中的数字部分,支持以下格式:
- 整数部分(如 "123")。
- 小数部分(如 ".123" 或 "123.456")。
- 指数部分(如 "1.23e4" 或 "1.23E-4")。
- 停止转换:遇到第一个不符合浮点数规则的字符时停止解析,后续字符将被忽略。
- 返回结果:将解析的数字转换为 double 类型返回。
- 错误处理:
- 完全成功转换:
- endptr 指向字符串末尾的空字符 '\0',可通过 if (*endptr == '\0') 验证。
- 示例:"3.1415926", NULL → 返回 3.141593。
- 部分成功转换:
- endptr 指向第一个非数字字符,可通过 if (*endptr != '\0') 验证。
- 示例:"3.14abc", NULL → 返回 3.14,endptr 指向 'a'。
- 无效输入:
- endptr 指向原始字符串起始地址,可通过 if (endptr == str) 验证(str 为原始字符串指针)。
- 示例:"HelloWorld", NULL → 返回 0.0f,endptr 指向 'H'。
- 溢出处理:需结合 errno 和 endptr 进行判断。
- 正溢出:
- 返回 HUGE_VAL(表示正无穷大,inf),并设置 errno 为 ERANGE。
- 示例:"1e500", NULL → 返回 HUGE_VAL。
- 负溢出:
- 返回 -HUGE_VAL(表示负无穷大,-inf),并设置 errno 为 ERANGE。
- 示例:"-1e500", NULL → 返回 -HUGE_VAL。
- 下溢:
- 返回 0.0,并可能设置 errno 为 ERANGE(具体行为取决于实现)。
- 示例:"1e-500", NULL → 返回 0.0。
- 正溢出:
- 完全成功转换:
转换示例:
输入字符串 | endptr 参数 | 返回值 | 说明 |
---|---|---|---|
"3.1415926" | NULL | 3.141593 | 完全成功转换 |
"-2.71828" | NULL | -2.718280 | 完全成功转换(负数) |
" +123.456" | NULL | 123.456000 | 忽略前导空格 |
"3.14abc" | NULL | 3.140000 | 部分成功转换,endptr 指向 'a' |
"HelloWorld" | NULL | 0.000000 | 无效输入,endptr 指向 'H' |
"1.23e4" | NULL | 12300.000000 | 科学计数法转换 |
"1e500" | NULL | HUGE_VAL(inf) | 正溢出 |
"-1e500" | NULL | -HUGE_VAL(-inf) | 负溢出 |
"1e-500" | NULL | 0.000000 | 下溢 |
2.4 注意事项
- 错误检测:
- 方法:通过检查 endptr 是否等于原始字符串的起始地址(str)来判断转换是否完全失败。
- 细节:
- 若 endptr == str:表示未解析出任何有效数字(如输入为 "HelloWorld")。
- 若 *endptr != '\0':表示部分成功转换如输入为 "123.45abc",endptr 指向 'a')。
- 若 *endptr == '\0':表示完全成功转换(如输入为 "123.45")。
- 溢出处理:
- 条件:当输入的数值超出 double 的表示范围时,strtod() 会设置 errno 为 ERANGE。
- 行为:
- 正溢出:返回 HUGE_VAL(inf)。
- 负溢出:返回 -HUGE_VAL(-inf)。
- 建议:在调用 strtod() 前,应清除 errno(如 errno = 0),以便后续检测。
- 科学计数法支持:strtod() 支持科学计数法(如 "1.23e4" 或 "1.23E-4"),这是 atof() 等函数不具备的特性。
- 推荐使用:相比 atof(),strtod() 更安全,支持错误检查和更灵活的浮点数格式解析。
2.5 示例程序
#include <stdio.h>
#include <stdlib.h> // 必须包含这个头文件才能使用 strtod()
#include <errno.h> // 包含 errno 和 ERANGE
#include <float.h> // 包含 DBL_MAX 和 DBL_MIN
#include <math.h> // 包含 HUGE_VAL
int main()
{
char *endptr; // 用于存储转换结束的位置
double result; // 用于存储转换结果(改为 double 类型)
// 示例 1:基本转换
result = strtod("3.1415926", &endptr);
printf("转换结果1: %lf, 结束位置: %s\n", result, endptr); // 输出: 转换结果1: 3.141593, 结束位置: (null)
// 示例 2:带符号的数字
result = strtod("-2.71828", &endptr);
printf("转换结果2: %lf, 结束位置: %s\n", result, endptr); // 输出: 转换结果2: -2.718280, 结束位置: (null)
// 示例 3:带前导空格和符号
result = strtod(" +123.456", &endptr);
printf("转换结果3: %lf, 结束位置: %s\n", result, endptr); // 输出: 转换结果3: 123.456001, 结束位置: (null)
// 示例 4:部分有效的数字
result = strtod("3.14abc", &endptr);
printf("转换结果4: %lf, 结束位置: %s\n", result, endptr); // 输出: 转换结果4: 3.140000, 结束位置: abc
// 示例 5:无效输入
result = strtod("HelloWorld", &endptr);
printf("转换结果5: %lf, 结束位置: %s\n", result, endptr); // 输出: 转换结果5: 0.000000, 结束位置: HelloWorld
// 示例 6:科学计数法
result = strtod("1.23e4", &endptr);
printf("转换结果6: %lf, 结束位置: %s\n", result, endptr); // 输出: 转换结果6: 12300.000000, 结束位置: (null)
// 示例 7:正溢出检测
errno = 0; // 重置 errno, 清除之前的错误
result = strtod("1e500", &endptr);
if (errno == ERANGE)
{
printf(" errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
printf(" HUGE_VAL: %lf\n", HUGE_VAL); // 输出: HUGE_VAL: inf
printf(" DBL_MAX: %e, DBL_MIN: %e\n", DBL_MAX, DBL_MIN); // 输出: DBL_MAX: 1.797693e+308, DBL_MIN: 2.225074e-308
printf("转换结果7: 超出范围,返回 HUGE_VAL(inf): %lf\n", result); // 输出: 转换结果7: 超出范围,返回 HUGE_VAL(inf): inf
}
else
{
printf("转换结果7: %lf\n", result);
}
// 示例 8:负溢出检测
errno = 0; // 重置 errno, 清除之前的错误
result = strtod("-1e500", &endptr);
if (errno == ERANGE)
{
printf(" errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
printf(" -HUGE_VAL: %lf\n", -HUGE_VAL); // 输出: -HUGE_VAL: -inf
printf(" DBL_MAX: %e, DBL_MIN: %e\n", DBL_MAX, DBL_MIN); // 输出: DBL_MAX: 1.797693e+308, DBL_MIN: 2.225074e-308
printf("转换结果8: 超出范围,返回 -HUGE_VAL(-inf): %lf\n", result); // 输出: 转换结果8: 超出范围,返回 -HUGE_VAL(-inf): -inf
}
else
{
printf("转换结果8: %lf\n", result);
}
// 示例 9:下溢检测
errno = 0; // 重置 errno, 清除之前的错误
result = strtod("1e-500", &endptr);
if (errno == ERANGE)
{
printf(" errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
printf(" HUGE_VALF: %f, -HUGE_VALF: %f\n", HUGE_VALF, -HUGE_VALF); // 输出: HUGE_VALF: inf, -HUGE_VALF: -inf
printf(" DBL_MAX: %e, DBL_MIN: %e\n", DBL_MAX, DBL_MIN); // 输出: DBL_MAX: 1.797693e+308, DBL_MIN: 2.225074e-308
printf("转换结果9: 字符串表示的数值绝对值小于 DBL_MIN,返回 0.0: %f\n", result); // 输出: 转换结果9: 字符串表示的数值绝对值小于 DBL_MIN,返回 0.0: 0.000000
}
else
{
printf("转换结果9: %lf\n", result);
}
// 示例 10:检查转换是否完全成功
result = strtod("123.45abc", &endptr);
if (*endptr != '\0')
{
printf("转换结果10: %lf, 但字符串未完全转换\n", result); // 输出: 转换结果10: 123.450000, 但字符串未完全转换
}
else
{
printf("转换结果10: %lf\n", result);
}
return 0;
}
程序在 VS Code 中的运行结果如下所示:
3 strtold() 函数
3.1 函数原型
#include <stdlib.h> // 必须包含这个头文件才能使用 strtold()
#include <errno.h> // 包含 errno 和 ERANGE
#include <float.h> // 包含 LDBL_MAX 和 LDBL_MIN
#include <math.h> // 包含 HUGE_VALL
long double strtold(const char *nptr, char **endptr);
3.2 功能说明
strtold() 函数用于将字符串转换为长双精度浮点数(long double)。与 atof() 等函数相比,strtold() 提供了更灵活、安全的转换机制,支持完善的错误检查和转换结束位置定位。
- 参数:
- nptr:指向待转换的字符串(以空字符 \0 结尾)。
- endptr:用于存储转换结束位置的指针。如果不需要使用该信息,可以将其设置为 NULL。
- 返回值:
- 成功转换:返回对应的 long double 值。
- 无效输入:若未执行任何转换,返回 0.0L。
- 溢出情况:若转换结果超出 long double 的表示范围:
- 正溢出:返回 HUGE_VALL(表示正无穷大,inf),并设置 errno 为 ERANGE。
- 负溢出:返回 -HUGE_VALL(表示负无穷大,-inf),并设置 errno 为 ERANGE。
- 下溢情况:若转换结果绝对值过小(小于 LDBL_MIN),返回 0.0L,并设置 errno 为 ERANGE(但某些实现可能不会设置 errno,具体行为取决于实现)。
错误处理与范围检查相关概念:
- errno
- 含义:errno 是一个全局变量(类型为 int,定义在 <errno.h> 头文件中),用于存储最近一次函数调用产生的错误代码。
- 作用:当 strtold() 等函数检测到错误(如数值溢出或下溢)时,会将 errno 设置为特定的错误码(如 ERANGE),以指示错误类型。
- 使用建议:在调用 strtold() 之前,应清除 errno(例如,通过 errno = 0),以便在转换后能够正确检测到是否发生了错误。
- ERANGE
- 含义:RANGE 是一个宏常量(通常值为 34,定义在 <errno.h> 头文件中),表示 “结果超出范围”(Range Error)。
- 使用场景:当 strtold() 检测到输入的字符串表示的数值超出 long double 的表示范围(过大或过小)时,会将 errno 设置为 ERANGE,表明发生了溢出或下溢错误。
- HUGE_VALL
- 含义:HUGE_VALL 是一个宏常量(定义在 <math.h> 头文件中),表示正无穷大(inf)的 long double 值。
- 使用场景:当 strtold() 发生正溢出时,返回 HUGE_VALL(inf)。
- LDBL_MAX 和 LDBL_MIN
- 含义:LDBL_MAX 和 LDBL_MIN 是宏定义(定义在 <float.h> 头文件中),分别表示 long double 类型的最大值和最小正规范化值。
- 使用场景:
- 若输入字符串表示的数值超过 LDBL_MAX,strtold() 会返回 HUGE_VALL(inf)并设置 errno 为 ERANGE。
- 若输入字符串表示的数值绝对值小于 LDBL_MIN,strtold() 会返回 0.0L 并可能设置 errno 为 ERANGE(取决于实现)。
3.3 转换规则
- 忽略前导空格:在解析字符串时,自动跳过字符串开头处的所有空白字符,包括空格(' ')、制表符('\t')、换行符('\n')等。
- 识别正负号:支持在数字前添加可选的符号字符:
- '+' 表示最终数值为正数。
- '-' 表示最终数值为负数。
- 若未指定符号,默认数值为正数。
- 读取数字:解析字符串中的数字部分,支持以下格式:
- 整数部分(如 "123")。
- 小数部分(如 ".123" 或 "123.456")。
- 指数部分(如 "1.23e4" 或 "1.23E-4")。
- 停止转换:遇到第一个不符合浮点数规则的字符时停止解析,后续字符将被忽略。
- 返回结果:将解析的数字转换为 long double 类型返回。
- 错误处理:
- 完全成功转换:
- endptr 指向字符串末尾的空字符 '\0',可通过 if (*endptr == '\0') 验证。
- 示例:"3.1415926535", NULL → 返回 3.141593。
- 部分成功转换:
- endptr 指向第一个非数字字符,可通过 if (*endptr != '\0') 验证。
- 示例:"3.14abc", NULL → 返回 3.14,endptr 指向 'a'。
- 无效输入:
- endptr 指向原始字符串起始地址,可通过 if (endptr == str) 验证(str 为原始字符串指针)。
- 示例:"HelloWorld", NULL → 返回 0.0f,endptr 指向 'H'。
- 溢出处理:需结合 errno 和 endptr 进行判断。
- 正溢出:
- 返回 HUGE_VALL(表示正无穷大,inf),并设置 errno 为 ERANGE。
- 示例:"1e5000", NULL → 返回 HUGE_VALL。
- 负溢出:
- 返回 -HUGE_VALL(表示负无穷大,-inf),并设置 errno 为 ERANGE。
- 示例:"-1e5000", NULL → 返回 -HUGE_VALL。
- 下溢:
- 返回 0.0f,并可能设置 errno 为 ERANGE(具体行为取决于实现)。
- 示例:"1e-5000", NULL → 返回 0.0L。
- 正溢出:
- 完全成功转换:
转换示例:
输入字符串 | endptr 参数 | 返回值 | 说明 |
---|---|---|---|
"3.1415926535" | NULL | 3.141593L | 完全成功转换 |
"-2.718281828" | NULL | -2.718282L | 完全成功转换(负数) |
" +123.456" | NULL | 123.456000L | 忽略前导空格 |
"3.14abc" | NULL | 3.140000L | 部分成功转换,endptr 指向 'a' |
"HelloWorld" | NULL | 0.000000L | 无效输入,endptr 指向 'H' |
"1.23e4" | NULL | 12300.000000L | 科学计数法转换 |
"1e5000" | NULL | HUGE_VALL(inf) | 正溢出 |
"-1e5000" | NULL | -HUGE_VALL(-inf) | 负溢出 |
"1e-5000" | NULL | 0.000000L | 下溢 |
3.4 注意事项
- 错误检测:
- 方法:通过检查 endptr 是否等于原始字符串的起始地址(str)来判断转换是否完全失败。
- 细节:
- 若 endptr == str:表示未解析出任何有效数字(如输入为 "HelloWorld")。
- 若 *endptr != '\0':表示部分成功转换如输入为 "123.45abc",endptr 指向 'a')。
- 若 *endptr == '\0':表示完全成功转换(如输入为 "123.45")。
- 溢出处理:
- 条件:当输入的数值超出 long double 的表示范围时,strtold() 会设置 errno 为 ERANGE。
- 行为:
- 正溢出:返回 HUGE_VALL(inf)。
- 负溢出:返回 -HUGE_VALL(-inf)。
- 建议:在调用 strtold() 前,应清除 errno(如 errno = 0),以便后续检测。
- 科学计数法支持:strtold() 支持科学计数法(如 "1.23e4" 或 "1.23E-4"),这是 atof() 等函数不具备的特性。
- 推荐使用:相比 atof(),strtold() 更安全,支持错误检查和更灵活的浮点数格式解析。
3.5 示例程序
#include <stdio.h>
#include <stdlib.h> // 必须包含这个头文件才能使用 strtold()
#include <errno.h> // 包含 errno 和 ERANGE
#include <float.h> // 包含 LDBL_MAX 和 LDBL_MIN
#include <math.h> // 包含 HUGE_VALL
int main()
{
char *endptr; // 用于存储转换结束的位置
long double result; // 用于存储转换结果(改为 long double 类型)
// 示例 1:基本转换
result = strtold("3.1415926535", &endptr);
printf("转换结果1: %Lf, 结束位置: %s\n", result, endptr); // 输出: 转换结果1: 3.141593, 结束位置: (null)
// 示例 2:带符号的数字
result = strtold("-2.718281828", &endptr);
printf("转换结果2: %Lf, 结束位置: %s\n", result, endptr); // 输出: 转换结果2: -2.718282, 结束位置: (null)
// 示例 3:带前导空格和符号
result = strtold(" +123.456", &endptr);
printf("转换结果3: %Lf, 结束位置: %s\n", result, endptr); // 输出: 转换结果3: 123.456000, 结束位置: (null)
// 示例 4:部分有效的数字
result = strtold("3.14abc", &endptr);
printf("转换结果4: %Lf, 结束位置: %s\n", result, endptr); // 输出: 转换结果4: 3.140000, 结束位置: abc
// 示例 5:无效输入
result = strtold("HelloWorld", &endptr);
printf("转换结果5: %Lf, 结束位置: %s\n", result, endptr); // 输出: 转换结果5: 0.000000, 结束位置: HelloWorld
// 示例 6:科学计数法
result = strtold("1.23e4", &endptr);
printf("转换结果6: %Lf, 结束位置: %s\n", result, endptr); // 输出: 转换结果6: 12300.000000, 结束位置: (null)
// 示例 7:正溢出检测
errno = 0; // 重置 errno, 清除之前的错误
result = strtold("1e5000", &endptr);
if (errno == ERANGE)
{
printf(" errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
printf(" HUGE_VALL: %Lf\n", HUGE_VALL); // 输出: HUGE_VALL: inf
printf(" LDBL_MAX: %Le, LDBL_MIN: %Le\n", LDBL_MAX, LDBL_MIN); // 输出: LDBL_MAX: 1.189731e+4932, LDBL_MIN: 3.362103e-4932
printf("转换结果7: 超出范围,返回 HUGE_VALL(inf): %Lf\n", result); // 输出: 转换结果7: 超出范围,返回 HUGE_VALL(inf): inf
}
else
{
printf("转换结果7: %Lf\n", result);
}
// 示例 8:负溢出检测
errno = 0; // 重置 errno, 清除之前的错误
result = strtold("-1e5000", &endptr);
if (errno == ERANGE)
{
printf(" errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
printf(" -HUGE_VALL: %Lf\n", -HUGE_VALL); // 输出: -HUGE_VALL: -inf
printf(" LDBL_MAX: %Le, LDBL_MIN: %Le\n", LDBL_MAX, LDBL_MIN); // 输出: LDBL_MAX: 1.189731e+4932, LDBL_MIN: 3.362103e-4932
printf("转换结果8: 超出范围,返回 -HUGE_VALL(-inf): %Lf\n", result); // 输出: 转换结果8: 超出范围,返回 -HUGE_VALL(-inf): -inf
}
else
{
printf("转换结果8: %Lf\n", result);
}
// 示例 9:下溢检测
errno = 0; // 重置 errno, 清除之前的错误
result = strtold("1e-5000", &endptr);
if (errno == ERANGE)
{
printf(" errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
printf(" HUGE_VALF: %f, -HUGE_VALF: %f\n", HUGE_VALF, -HUGE_VALF); // 输出: HUGE_VALF: inf, -HUGE_VALF: -inf
printf(" LDBL_MAX: %Le, LDBL_MIN: %Le\n", LDBL_MAX, LDBL_MIN); // 输出: LDBL_MAX: 1.189731e+4932, LDBL_MIN: 3.362103e-4932
printf("转换结果9: 字符串表示的数值绝对值小于 LDBL_MIN,返回 0.0L: %Lf\n", result); // 输出: 转换结果9: 字符串表示的数值绝对值小于 LDBL_MIN,返回 0.0L: 0.000000
}
else
{
printf("转换结果9: %Lf\n", result);
}
// 示例 10:检查转换是否完全成功
result = strtold("123.45abc", &endptr);
if (*endptr != '\0')
{
printf("转换结果10: %Lf, 但字符串未完全转换\n", result); // 输出: 转换结果10: 123.450000, 但字符串未完全转换
}
else
{
printf("转换结果9: %Lf\n", result);
}
return 0;
}
程序在 VS Code 中的运行结果如下所示:
4 字符串转数值函数总结
函数名 | 功能 | 返回值类型 | 转换范围 | 适用场景 | 错误处理(errno) |
---|---|---|---|---|---|
strtof | 将字符串转换为单精度浮点型(float) | float | FLT_MIN 到 FLT_MAX(通常为 1.175494e-38 到 3.402823e+38) | 需要处理单精度浮点数的转换 | 无效输入时返回 0.0f; 正溢出时返回 +HUGE_VALF(inf); 负溢出时返回 -HUGE_VALF(-inf); 下溢时可能返回 0.0f 或次正规数(取决于实现),并设置 errno 为 ERANGE(仅溢出时设置) |
strtod | 将字符串转换为双精度浮点型(double) | double | DBL_MIN 到 DBL_MAX(通常为 2.225074e-308 到 1.797693e+308) | 需要处理双精度浮点数的转换 | 无效输入时返回 0.0; 正溢出时返回 +HUGE_VAL(inf); 负溢出时返回 -HUGE_VAL(-inf); 下溢时可能返回 0.0 或次正规数(取决于实现),并设置 errno 为 ERANGE(仅溢出时设置) |
strtold | 将字符串转换为扩展精度浮点型(long double) | long double | LDBL_MIN 到 LDBL_MAX(范围因平台而异,通常更大) | 需要处理高精度或大范围浮点数的转换 | 无效输入时返回 0.0L; 正溢出时返回 +HUGE_VALL(inf); 负溢出时返回 -HUGE_VALL(-inf); 下溢时可能返回 0.0L 或次正规数(取决于实现),并设置 errno 为 ERANGE(仅溢出时设置) |
-
头文件:所有这些函数都定义在 <stdlib.h> 头文件中,使用时需要包含该头文件。
-
错误处理细节:
- 当转换结果超出目标类型的最大正值时,函数返回 +HUGE_VALF、+HUGE_VAL 或 +HUGE_VALL(取决于函数类型),并设置 errno 为 ERANGE。
- 当转换结果小于目标类型的最小负值时,函数返回 -HUGE_VALF、-HUGE_VAL 或 -HUGE_VALL(取决于函数类型),并设置 errno 为 ERANGE。
- 当转换结果非常接近零,但仍在目标类型的范围内时,函数可能返回 0.0、0.0f、0.0L 或一个次正规数(即一个非常小的非零值,其精度可能低于正常范围的数值)。下溢通常不会设置 errno 为 ERANGE,除非在某些特定实现中明确规定。
-
跨平台兼容性:
- 这些函数是 C 标准库的一部分,因此在大多数平台上都可用。
- 浮点类型的范围(如 FLT_MAX、DBL_MAX、LDBL_MAX)和精度(如 FLT_DIG、DBL_DIG、LDBL_DIG)可能因平台和编译器实现而异,因此在跨平台开发时需要注意这些差异。