C:查找算术表达式类型的最大值和最小值

时间:2011-10-31 14:44:03

标签: c types expression max min

我需要找到没有副作用的任意C表达式的最大值和最小值。以下宏可以在我的机器上运行。他们会在所有平台上工作吗?如果没有,他们可以修改工作吗?我的意图是随后使用这些来实现SAFE_MUL(a,b)之类的宏来代替a*bSAFE_MUL将包括检查乘法溢出。

编辑:按史蒂夫的建议投射类型。

#include <stdio.h>
#include <limits.h>

#define IS_SIGNED(exp) (((exp)*0-1) < 0)

#define TYPE_MAX_UNSIGNED(exp) ((exp)*0-1)

#define TYPE_MAX_SIGNED(exp) ( \
    sizeof (exp) == sizeof (int) \
    ? \
    INT_MAX \
    : \
    ( \
        sizeof (exp) == sizeof (long) \
        ? \
        LONG_MAX \
        : \
        LLONG_MAX \
    ) \
)

#define TYPE_MAX(exp) ((unsigned long long)( \
    IS_SIGNED (exp) \
    ? \
    TYPE_MAX_SIGNED (exp) \
    : \
    TYPE_MAX_UNSIGNED (exp) \
))

#define TYPE_MIN_SIGNED(exp) ( \
    sizeof (exp) == sizeof (int) \
    ? \
    INT_MIN \
    : \
    ( \
        sizeof (exp) == sizeof (long) \
        ? \
        LONG_MIN \
        : \
        LLONG_MIN \
    ) \
)

#define TYPE_MIN(exp) ((long long)( \
    IS_SIGNED (exp) \
    ? \
    TYPE_MIN_SIGNED (exp) \
    : \
    (exp)*0 \
))

int
main (void) {

    printf ("TYPE_MAX (1 + 1) = %lld\n", TYPE_MAX (1 + 1));
    printf ("TYPE_MAX (1 + 1L) = %lld\n", TYPE_MAX (1 + 1L));
    printf ("TYPE_MAX (1 + 1LL) = %lld\n", TYPE_MAX (1 + 1LL));
    printf ("TYPE_MAX (1 + 1U) = %llu\n", TYPE_MAX (1 + 1U));
    printf ("TYPE_MAX (1 + 1UL) = %llu\n", TYPE_MAX (1 + 1UL));
    printf ("TYPE_MAX (1 + 1ULL) = %llu\n", TYPE_MAX (1 + 1ULL));
    printf ("TYPE_MIN (1 + 1) = %lld\n", TYPE_MIN (1 + 1));
    printf ("TYPE_MIN (1 + 1L) = %lld\n", TYPE_MIN (1 + 1L));
    printf ("TYPE_MIN (1 + 1LL) = %lld\n", TYPE_MIN (1 + 1LL));
    printf ("TYPE_MIN (1 + 1U) = %llu\n", TYPE_MIN (1 + 1U));
    printf ("TYPE_MIN (1 + 1UL) = %llu\n", TYPE_MIN (1 + 1UL));
    printf ("TYPE_MIN (1 + 1ULL) = %llu\n", TYPE_MIN (1 + 1ULL));
    return 0;
}

2 个答案:

答案 0 :(得分:3)

  • IS_SIGNED宏并不能说明小于int的无符号类型。 IS_SIGNED((unsigned char)1)在任何正常实施中均属实,因为(unsigned char)1*0的类型为int,而不是unsigned char

您的最终SAFE宏仍应说明是否发生溢出,因为相同的整数提升适用于所有算术。但是他们会告诉你是否在乘法中出现溢出,必然是否在用户将结果转换回其中一个操作数的原始类型时发生。

考虑到这一点,你可能已经知道了,因为你的宏不会尝试建议CHAR_MIN等等。但是将来发现这个问题的其他人可能没有意识到这种限制。

  • 没有一种类型可以保证能够保存TYPE_MINTYPE_MAX可以评估的所有值。但是,您可以TYPE_MAX始终评估为unsigned long long(并且值始终适合该类型),TYPE_MINsigned long long也一样。这将允许您使用正确的printf格式,而无需使用您对表达式是否已签名的了解。目前TYPE_MAX(1)long long,而TYPE_MAX(1ULL)unsigned long long

  • 从技术上讲,允许intlong具有相同的大小但不同的范围,因为long的填充位数少于int。我怀疑任何重要的实现都是这样做的。

答案 1 :(得分:1)

只是一个想法:如果你使用gcc,你可以使用它的typeof扩展名:

#define IS_SIGNED(exp) ((typeof(exp))-1 < 0)
#define TYPE_MAX_UNSIGNED(exp) ((typeof(exp))-1)
#define TYPE_MAX_SIGNED(exp) ... // i cannot improve your code here

编辑:也可能会检查浮点类型:

#define CHECK_INT(exp) ((typeof(exp))1 / 2 == 0)
#define CHECK_INT(exp) (((exp) * 0 + 1) / 2 == 0) // if cannot use typeof
#define MY_CONST_1(exp) (1/CHECK_INT(exp))
// Now replace any 1 in code by MY_CONST_1(exp) to cause error for floating-point