我有一个文件,其中包含表示float
和uint64_t
值的字符串。
我确切地知道哪个字符串包含float
值,哪个字符串包含uint64_t
值 - 这不是我面临的问题。
以下是我将它们转换为各自数据类型的方法:
char* t, v;
uint64_t cn;
cn = strtoull(t, &v, 10);
char* tt, vv;
float cn2;
cn2 = strtof(tt, vv);
但是问题出现在我想捕获的以下边缘情况中:
假设uint64_t
的字符串是"99999999999999999999999999999999999999999999999999"
这不能在 8 个字节内表示,因此会导致溢出,从而导致cn = 18446744073709551615
。
float cn2
也有同样的问题.
如何捕捉这种行为?
strtoull
指示值超出范围。请考虑以下代码:
#include <errno.h>
…
errno = 0; // Set error code to zero before call.
unsigned long long x = strtoull(t, &v, 10);
if (errno == ERANGE)
{
// Handle out-of-range error.
}
如果t
中的数字太大,这将达到错误情况。
请注意,如果t
包含减号(但幅度不太大),则strtoull
将返回一个在unsigned long long
类型中"否定"的值(即,将ULLONG_MAX
+1 括起来的值),即使负值超出类型范围;不会提供错误指示。因此,如果要检测所有超出范围的情况,则必须检查t
是否有带有非零返回值的减号(可能在前导空格之后)。
strtof
提供了足够的信息来区分病例,根据C 2018 7.22.1.3 10:
- 如果该值为正且太大,则返回
HUGE_VALF
并将errno
设置为ERANGE
。 - 如果该值为负且太大,则返回
-HUGE_VALF
并将errno
设置为ERANGE
。 - 如果该值低于正常范围,则返回一个小值,并将
errno
设置为ERANGE
。
根据此strtoull
引用,当发生溢出时,它将返回ULLONG_MAX
并且errno
设置为ERANGE
。
因此,您应该在调用之前将errno
设置为零,之后可以检查ULLONG_MAX
并errno == ERANGE
以查看是否发生溢出。
与strtof
类似,它将返回HUGE_VALF
.
如果小于 0 的值需要检测
strtoull("-1", ..., ...)
"换行"并返回ULLONG_MAX
.
strto(u)ll()
转换为至少64 位整数。 规划unsigned long long
可能超过 64 位的未来增长。
例:
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
uint64_t convert_str_uint64(const char *s, char **endptr, int base) {
errno = 0;
long long ival = strtoll(s, endptr, base);
// Look for negative numbers
if (ival < 0) {
errno = ERANGE;
return 0;
}
// We are done for many positive numbers
if (*endptr > s && ival <= INT64_MAX && errno == 0) {
return (uint64_t) ival;
}
// Input may still be a valid value more than INT64_MAX.
errno = 0;
unsigned long long uval = strtoull(s, endptr, base);
#if ULLONG_MAX > UINT64_MAX
if (uval > UINT64_MAX) {
uval = UINT64_MAX;
errno = ERANGE;
}
#endif
return (uint64_t) uval;
}