我想要制作比较数字的strcmp-like接口,例如,在C中返回int > 0 if x > y、0 if x = y、< 0 if x < y的ncmp(x, y) (而不是C++)。
虽然我不一定要限制这些类型,但我的主要兴趣是比较signed long ints和doubles,“接口”可以是宏,比如tgmath.h,也可以是(一组)函数。我希望所有对signed long int和double都能工作;例如,(signed long int, double)和(double, double)应该可以工作。
我目前使用的是以下宏:
#define ncmp(x, y) ((x) > (y)) - ((x) < (y))这个天真的宏有什么缺陷吗?有没有一个更好的,稳健的解决方案来比较数字?
任何帮助都将不胜感激!
发布于 2021-03-31 12:32:12
,我希望所有的
signed long int和double都能工作;
由于C11,代码可以使用_Generic来引导基于类型的函数选择。
int cmp_long(long x, long y) {
return (x > y) - (x < y);
}
int cmp_double(double x, double y) {
return (x > y) - (x < y);
}
#define cmp(X, Y) _Generic((X) - (Y), \
long: cmp_long((X),(Y)), \
double: cmp_double((X),(Y)) \
)这种方法不能很好地检测X, Y属于不同类型的情况,因为(X) - (Y)使用它们之间的公共类型( @Ian Abbott )。然而,这是一个开始。
int main(void) {
printf("%d\n", cmp(1L, 2L));
printf("%d\n", cmp(3.0, 4.0));
}可以制作一种更复杂的两阶段_Generic来区分long, double和double, long.我把那部分留给OP。
比较函数如下所示。与long相比,棘手的部分是不丢失double的精度(可能是64位)。
// TBD: handling of NANs
#define DBL_LONG_MAX_P1 ((LONG_MAX/2 + 1)*2.0)
int cmp_long_double(long x, double y) {
// These 2 compares are expected to be exact - no rounding
if (y >= DBL_LONG_MAX_P1) return -1;
if (y < (double)LONG_MIN) return 1;
// (long) y is now in range of `long`. (Aside from NANs)
long y_long = (long) y; // Lose the fraction
if (y_long > x) return -1;
if (y_long < x) return 1;
// Still equal, so look at fraction
double whole;
double fraction = modf(y, &whole);
if (fraction > 0.0) return -1;
if (fraction < 0.0) return 1;
return 0;
}可能存在简化。
当double准确地编码所有long或当long double存在并对所有long进行精确编码时,最容易将long和double转换为公共类型并进行比较。
发布于 2021-03-31 13:50:46
对于这个宏:
#define ncmp(x, y) ((x) > (y)) - ((x) < (y))主要问题是:
#定义ncmp(x,y) (X)> (y)) - ((x) <(Y)
(x)和(y)进行了两次评估,如果评估有副作用,这可能是一个问题。为了避免多重计算的问题,宏展开可以使用泛型选择表达式为每种类型的比较调用不同的函数。
注释1:在2011年版本的C标准(C11)中添加了泛型选择。
下面是一个使用泛型选择的宏示例。它可能需要扩展以支持其他类型:
#define ncmp(x, y) _Generic((x) < (y), \
int: ncmp_si, \
unsigned: ncmp_ui, \
long: ncmp_sli, \
unsigned long: ncmp_uli, \
long long: ncmp_slli, \
unsigned long long: ncmp_ulli, \
float: ncmp_f, \
double: ncmp_d, \
long double: ncmp_ld \
)((x), (y))注释2:不计算泛型选择(
(x) < (y))的控制表达式,但它的类型用于选择相应的泛型关联表达式(如果有的话)。注意3:在控制表达式中选择<并不重要,但至少要检查(x)和(y)是否有有序的关系。对于算术操作数,控制表达式的类型是通常算术转换的结果。注4:由于通常在控制表达式中对<的操作数进行算术转换,因此不需要为int级别以下的整数类型添加大小写。注5:可以添加default:泛型关联。例如,可以将其定义为回到使用不太安全的多重评估方法,如下所示:
#define ncmp(x, y) _Generic((x) < (y), \
int: ncmp_si((x), (y)), \
unsigned: ncmp_ui((x), (y)), \
long: ncmp_sli((x), (y)), \
unsigned long: ncmp_uli((x), (y)), \
long long: ncmp_slli((x), (y)), \
unsigned long long: ncmp_ulli((x), (y)), \
float: ncmp_f((x), (y)), \
double: ncmp_d((x), (y)), \
long double: ncmp_ld((x), (y)), \
default: ((x) > (y)) - ((x) < (y)) \
),但我选择由程序员来添加缺少的情况。
有必要定义上述每个泛型关联所使用的函数。为了保存一些类型,可以定义一个助手宏来定义它们:
#define MK_NCMP_(suf, T) \
static inline int ncmp_##suf(T x, T y) { return (x > y) - (x < y); }
MK_NCMP_(si, int)
MK_NCMP_(ui, unsigned)
MK_NCMP_(sli, long)
MK_NCMP_(uli, unsigned long)
MK_NCMP_(slli, long long)
MK_NCMP_(ulli, unsigned long long)
MK_NCMP_(f, float)
MK_NCMP_(d, double)
MK_NCMP_(ld, long double)https://stackoverflow.com/questions/66887241
复制相似问题