微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

用于比较 C 中数字的类似 strcmp 的接口

如何解决用于比较 C 中数字的类似 strcmp 的接口

我想制作类似 strcmp 的接口来比较数字,例如,ncmp(x,y) 返回 int > 0 if x > y,0 if {{ 1}},x = y if < 0 在 C(不是 C++)中。

虽然我不一定想限制类型,但我的主要兴趣是比较 x < ysigned long int。 “接口”可以是 double 中的宏,也可以是(一组)函数。我希望所有对 tgmath.hsigned long int 都能工作;例如,double(signed long int,double) 应该可以工作。

我目前使用的是以下宏:

(double,double)

这个天真的宏有什么陷阱吗? 是否有更好、更可靠的解决方案来比较数字?

任何帮助将不胜感激!

解决方法

我希望所有的 signed long intdouble 都能正常工作;

从 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));
}

可以使用更复杂的 2 阶段 _Generic 来区分 long,doubledouble,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 和 {{1} } 到通用类型并进行比较。

,

对于这个宏:

#define ncmp(x,y) ((x) > (y)) - ((x) < (y))

主要问题是:

  1. 它需要扩展中的一组额外括号才能形成主表达式。扩展中没有足够的括号将其转换为主表达式。应该是:

    #define ncmp(x,y) (((x) > (y)) - ((x) < (y)))
    
  2. 它计算了 (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 等级的整数类型添加 case。

注 5:可以添加 default: 通用关联。例如,它可以定义为使用安全性较低的多重评估方法,如下所示:

#define ncmp(x,\
    int: ncmp_si((x),(y)),\
    unsigned: ncmp_ui((x),\
    long: ncmp_sli((x),\
    unsigned long: ncmp_uli((x),\
    long long: ncmp_slli((x),\
    unsigned long long: ncmp_ulli((x),\
    float: ncmp_f((x),\
    double: ncmp_d((x),\
    long double: ncmp_ld((x),\
    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)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。