【问题标题】:Validate parameter for 0 or 1验证 0 或 1 的参数
【发布时间】:2015-08-02 10:19:07
【问题描述】:

我有一个int 类型的参数num,用户可以给它以下两个值之一:01
我可以用显而易见的方法来检查它:

if (num < 0 || num > 1)
    print("The parameter value is incorrect.\n");

但我想知道是否有更好的(更快?更少的代码?)来做到这一点?

编辑
这是一些数据流代码,因此性能至关重要。我正在寻找一种更快的方法来运行此检查。

谢谢

【问题讨论】:

  • 在代码中冗长是可以的 - 它通常使其更具可读性 - 因此可维护
  • 优化这个的动机是什么?为什么要降低代码的可读性、难以维护和可能不那么健壮?
  • @PaulR - 这个问答的动机是,如果有人需要在数据流路径中运行这种检查,其中每个额外的 if!=&lt; 占用额外的(宝贵的)时间,他们将有地方查找它。我实际上并没有检查这里建议的不同解决方案的运行时间,但我很确定x &amp; ~1x!=0 &amp;&amp; x!=1 快。
  • 是的,请停止这种混淆。调试/维护/增强自己的代码已经够糟糕了,但是如果你从其他人那里继承了这种东西,除了源代码级别的明显可见之外,你什么都不想看到。
  • @shapiro.yaacov:您对性能的假设很可能是错误的(取决于 CPU 和编译器),并且您犯了不首先测量性能的主要罪行。这通常被称为过早优化,有很多很好的理由可以避免它。首先进行分析,确定热点,然后仅在绝对必要时进行优化。

标签: c


【解决方案1】:

我会继续清晰而不是减少字符:

if (num != 0 && num != 1){
    print("The parameter value is incorrect.\n");
}

当您在凌晨 2 点调试程序时,您最不想做的就是过度考虑范围和按位运算。

【讨论】:

  • 哦,是的!在似乎是“异或交换和更糟”的月份有点理智:)
【解决方案2】:

通过(幼稚的)微优化清除代码

您基本上对实际编译器的行为做出了错误的假设。在这两种情况下,即:

if (num < 0 || num > 1) { ...

if (num != 0 && num != 1) { ...

optimizing compiler 无论如何都会将其缩减为最短的形式。您可能会看到,两者都生成相同的程序集,可能看起来像(x86 平台):

cmp    $0x1,%eax
jbe    1e <foo+0x1e> # jump if below or equal

这已经足够快了,因为所有主要架构上的cmp 指令都有 latency of one cycle

底线是选择任何代码,让您、未来的维护者清楚您的意图并让编译器完成其工作。只需确保您将其设置为适当的优化级别(例如-O2 或更高级别)。


辅助分支预测

但是,如果性能在这里真的很重要(而且您也这样描述它,不是吗?),那么您可以考虑另一种优化,即 branch prediction 级别(假设您的 CPU 支持为了它)。 GCC 具有 __builtin_expect 内在函数,允许提示编译器,在大多数情况下,是否会采用分支。

您可以使用__builtin_expect 为编译器提供分支 预测信息。一般来说,您应该更喜欢使用实际 对此(-fprofile-arcs)的配置文件反馈,因为程序员是 众所周知,他们不擅长预测他们的程序的实际执行情况。 但是,有些应用程序很难收集这些数据。

例如,如果您有信心,该函数在大约 99% 的情况下采用 01,那么您可以将其写为:

#define unlikely(x) __builtin_expect((x), 0)

if (unlikely(num != 0 && num != 1)) { ...

【讨论】:

  • 感谢您的回答。我在想num &amp; ~1 会更快。
  • @shapiro.yaacov:它可能不会更快。 num &amp; ~1 可以编译为test $0xfffffffe,%eax,但cmptest 仍然是非常基本的指令,只有一个周期的延迟。如需更多参考,请参阅Agner's Instruction tables
【解决方案3】:
if (!!num == num)
{
   /* value is either a zero or 1 */
}

!!会将其他值更改为 0 或 1,因此如果您尝试传递 5:

if (1 == 5) // FALSE:

【讨论】:

    【解决方案4】:

    你可以使用移位运算符

    if(num>>1) print("The parameter value is incorrect.\n");
    

    【讨论】:

      【解决方案5】:

      由于唯一可以点亮的位是第一个位,因此检查其余位是否已关闭就可以了。

      另外,由于它只是1,它的否定是(在c 语法中):~1

      所以:

      if (num & ~1)
          print("The parameter value is incorrect.\n");
      

      【讨论】:

      • 你为什么要像这样混淆你的代码?
      • @PaulR:问题是关于一种更简单的检查方法,而不是是否混淆了恕我直言,但关键是答案是错误的! @shapiro:代码错误,也许你打算使用右移位运算符:if (num &gt;&gt; 1) printf("The parameter value is incorrect.\n");
      • @Frankie_C - 如果我可能会问,答案有什么不正确的?也可以使用右移运算符,但对于01,不会打印错误。 1 &amp;~1false AFAIK
      • @shapiro.yaacov:对不起,你的回答是正确的。我的错。无论如何,没有一个反对票是我的。我再说一遍,答案与问题是一致的:简单的检查是否被混淆了......
      • 极端情况:对于使用晦涩的非 2 补码的机器,“因为唯一可以点亮的位是第一个”是不正确的,因为这些系统可能有 3 个可接受的位模式 1、0 , -0。 IMO 如果您正在测试位模式,这没关系。如果您正在测试数值,请使用范围比较,例如 &gt;=
      【解决方案6】:

      同意if (num &lt; 0 || num &gt; 1) { ... 是要走的路

      我想我会添加一个类似高尔夫球的代码

      if (num > 1u) { ...
      

      这会将int num 转换为unsigned,然后再与1u 进行比较。

      这种方法的一个弱点是,如果 num 的符号类型比 unsigned 更宽。在那种情况下,代码可能会让我们“陷入困境”

      if (num > 1ull) { ...
      

      最后,使用(num &lt; 0 || num &gt; 1)

      1. 生成最快的代码

      2. 生成糟糕的代码,OP 确实应该寻找更好的编译器来提高整体性能,而不是这样小的代码优化。

      【讨论】:

        【解决方案7】:

        有很多方法可以做到这一点。 我认为捷径并不总是正确的。 想想有时您可能需要通知用户他的输入,而不仅仅是说“参数值不正确”。 我,我有自己的函数,试试这个:

        #include<stdio.h>
        
        int checkInput(int min, int max);
        
        int main(void){
            int number = checkInput(0,1);
        
            printf("\nYour number is\t%d\n",number);
        
            return 0;
        }
        
        int checkInput(int min, int max){
            int option,check;
            char c;
        
            do{
                printf("Please type a number beetwen %d and %d:\t",min,max);
        
                if(scanf("%d%c",&option,&c) == 0 || c != '\n'){
                    while((check = getchar()) != EOF && check != '\n');
                    printf("\tI sayed a Number please\n\n");
                }else if(option < min || option > max){
                    printf("\tThe number has to be beetwen %d and %d\n\n",min,max);
                }else{
                    break;
                }
            }while(1);
        
            return option;
        }
        

        输出:

        Please type a number beetwen 0 and 1: 0k
            I sayed a Number please
        
        Please type a number beetwen 0 and 1:   1j
            I sayed a Number please
        
        Please type a number beetwen 0 and 1:   8
            The number has to be beetwen 0 and 1
        
        Please type a number beetwen 0 and 1:   1
        
        Your number is  1
        

        【讨论】:

        • 不错。 1) 极端情况:如果用户键入'\n',则没有检测到该空行的反馈。 2) 最好在printf("Please type ... 之后fflush(stdout),以确保提示已显示且未缓冲。 3) 建议从scanf() 中检测EOF,否则可能出现无限循环。
        • 这将是 EOF 上的无限循环。另一种方法:stackoverflow.com/a/21666135/2410359
        • @chux 你能告诉我一个demo 修改后的结果吗?
        • Je ne parle pas Java.
        • 很好,我不得不用谷歌翻译那个:D,从 Java 所在的那个角落的 Tab 菜单中选择 C ​​而不是 Java。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-09
        • 2011-11-30
        • 2020-07-02
        • 1970-01-01
        • 2014-03-02
        相关资源
        最近更新 更多