如果我正在从标准输入读取double类型的数字,我如何检查正在读取的数字是否实际上有效(这些数字实际上是 double)?
4 回答
您可以使用strtod
. endptr == nptr
根据手册页检查结果是否为零,然后检查是否为:
如果不执行转换,则返回零并将 nptr 的值存储在 endptr 引用的位置。
像这样的东西:
char input[50];
char * end;
double result = 0;
fgets(input, sizeof input, stdin);
errno = 0;
result = strtod(input, &end);
if(result == 0 && (errno != 0 || end == input)){
fprintf(stderr, "Error: input is not a valid double\n");
exit(EXIT_FAILURE);
}
编辑标准和手册页之间似乎有些差异。手册页说,endptr == nptr
当不执行转换时,虽然标准似乎暗示不一定是这种情况。更糟糕的是,它说在没有转换的情况下errno
可以设置为EINVAL
. 编辑示例代码以进行检查errno
。
或者,sscanf
可以与scanf
结合使用(优先于 )fgets
:
/* just fgetsed input */
if(sscanf(input, "%lf", &result) != 1){
fprintf(stderr, "Error: input is not a valid double\n");
exit(EXIT_FAILURE);
}
另外,不要忘记检查 for 的返回值fgets
,NULL
以防它失败!
既不简单strtod
也sscanf
不足以区分诸如1,5
或1blah
与期望1.0
的情况 - 所有这些都将导致1.0
. 原因是
、
strtod()
和函数将nptr 指向的字符串的初始部分分别转换为 double、float 和 long double 表示strtof()
形式。strtold()
要确保整个字符串是有效的双精度字面量,请strtod
像这样使用:
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
...
char *endptr;
errno = 0;
double result = strtod(input, &endptr);
if (errno != 0 || *endptr != '\0') {
fprintf(stderr, "the value could not be represented as a double exactly\n");
}
errno
如果无法表示该值,则将设置 ( ) ERANGE
。此外,end
将指向未转换的第一个字符。如果没有设置语言环境,在解析1,5
or时1blah
,endptr
将指向第二个字符。如果整个字符串被成功解析为双精度常量,*endptr
将指向终止的 '\0'。
请注意,在errno
调用函数之前必须将其设置为零,否则它将保留先前失败的函数调用的值。
我们如何检查输入字符串是否是有效的双精度?
从
strtod()
for double
、
strtof()
forfloat
和
strtold()
for开始long double
。
double strtod(const char * restrict nptr, char ** restrict endptr);
,
strtod
... 函数将指向的字符串的初始部分转换nptr
为double
....指向最终字符串的指针存储在 指向的对象中
endptr
,前提是endptr
它不是空指针。C11dr §7.22.1.3 2&5
简化代码以松散地检查有效性。不抱怨上溢/下溢或多余的文字。
// Return true on valid
bool valid_string_to_double(const char *s) {
char *end;
strtod(s, &end);
return s != end;
}
使用的挑战strto*()
包括:errno == RANGE
算术上溢和下溢 。溢出时的返回值仅在默认舍入模式下指定。该值可能是无穷大或很大的数字。下溢的返回值是实现定义的。 已知在 C 规范未指定的条件下设置为其他非零值。允许使用前导空格,不考虑尾随空格。HUGE_VAL
errno
查找 1) 转换、2) 额外空间、3) 上溢/下溢的示例函数。它不仅返回一个有效的指示,它还处理转换的值和errno
之后的状态。
// Return 0 on success
// Return non-0 on error, adjust these values as needed - maybe as an `enum`?
int convert_string_to_double(double *y, const char *s) {
char *end;
errno = 0;
*y = strtod(s, &end);
if (s == end) {
return 1; // Failed: No conversion, *y will be 0
}
// This may/may not constitute an error - adjust per coding goals
// Too great or too small (yet not exactly 0.0)
if (errno == ERANGE) {
if (fabs(*y) > 1.0) {
return 2; // Overflow
}
// In the case of too small, errno _may_ be set. See §7.22.1.3 10.
// For high consistency, return 0.0 and/or clear errno and/or return success.
// *y = 0.0; errno = 0;
}
// What to do if the remainder of the string is not \0?
// Since leading whitespace is allowed,
// let code be generous and tolerate trailing whitespace too.
while (isspace((unsigned char) *end)) {
end++;
}
if (*end) {
return 3; // Failed: Extra non-white-space junk at the end.
}
return 0; // success
}
如果结果下溢 (7.12.1),则函数返回一个值,其大小不大于返回类型中的最小归一化正数;是否
errno
获取值ERANGE
是实现定义的。C11dr §7.22.1.3 10
一个考虑因素包括errno
这个函数完成后的值。C 规范仅errno == ERANGE
用于strtod()
,但已知各种实现会errno
出于其他原因(包括“无转换”)设置为其他值。errno
除非ERANGE
为了高一致性,否则代码可以清晰。
您可以使用标准的 atof 功能。它在失败时返回 0 - 您可以预先测试字符串是否为 0。