【问题标题】:Finding the greater number without conditional statements在没有条件语句的情况下找到更大的数字
【发布时间】:2015-04-07 20:34:53
【问题描述】:

我正在尝试比较两个数字“a”和“b”,如果 a 大于 b,我想将另一个 double c 设置为 1,否则我想将 c 设置为 0。

以下是我到目前为止所做的-

#include <stdio.h>
#include <math.h>

int main() {
    double a = 0.01242;
    double b = 0.04231;
    double c = ceil(fmod(a,b));
    //c should be 1 if a > b or else it should be 0
}

我的问题是它适用于某些数字,但通常 ceil() 会返回大于 1 的值。

我想不使用任何包含布尔返回类型的运算符。

【问题讨论】:

  • 不确定您所说的“任何类型的包含布尔返回类型的运算符”是什么意思...三元运算符 (foo = bar ? 0 : 1;) 出来了吗?
  • 我不知道您认为避免“布尔返回类型”会达到什么效果,但您目前涉及fmod 的解决方案至少与除法一样昂贵。
  • ceil 里面有条件运算符吗
  • signbit() 是条件函数吗?

标签: c math


【解决方案1】:

我认为您可以使用以下表达式找到a和b之间的最大值,并使用max(a, b) ^ b找出它们是否相等,然后将max(a, b) ^ b的结果分配给c。(^是按位异或)

max(a, b) = 1 / 2. * (a + b + sqrt((a - b) * (a - b)));

max(a, b) = 1 / 2. * (a + b + fabs(a-b));

我们不能在 double 上使用 ^ 运算符,所以如果你想使用 double 跟随程序也许可以完成你的工作:

#include <stdio.h>
#include <math.h>

double max(double a, double b)
{
    return 1 / 2. * (a + b + fabs(a-b));
}

int main(int argc, char *argv[])
{
    double b = 0.01242;
    double a = 0.04231;

    printf("%g\n", max(a, b));

    int c = ceil(max(a, b) / b);
    c = (c + 1) % c;
    /* c should be 1 if a > b or else it should be 0 */
    printf("%d\n", c);
}

【讨论】:

  • 你的max(a,b) 函数没问题,但比较不是你根本不处理负值,也没有防止被零除的保护。对于非零正值,输出似乎是有效的
【解决方案2】:

有趣的问题我是这样解决的:

//---------------------------------------------------------------------------
double fpu_cmp(double a,double b)
    {
//  a> b -> c=1
//  a<=b -> c=0
    double c;           // a<=b     a>=b
    c=a-b;              // c<=0.0   c>0.0
    c/=fabs(c)+1e-323;  // c={-1,0} c=+1.0
    c+=fabs(c);         // c=0.0    c=+2.0
    c*=0.5;             // c=0.0    c=+1.0
    return c;
    }

//---------------------------------------------------------------------------
  • 同时处理正值和负值
  • rems 解释了它的工作原理
  • 第一列是c 中的内容,如果a&lt;=b
  • 如果a&gt;b,第二列是c 中的内容
  • 在间隔 a,b=&lt;-3,+3&gt; 上进行了测试,步骤为 0.01,没有错误
  • 如果您担心1e-323 常量会将1.0 更改为0.99999999...9
  • 那么您仍然可以使用return ceil(c);,但我认为在当前状态下已经可以了

[注释]

  • fabs 可以用一位操作完成
  • c*=0.5 只是指数减量(位屏蔽)
  • 所以唯一缓慢的操作是 FPU 划分
  • 1e-323 只是为了避免被零除(但如果比较非常接近的数字会显着改变结果)这个常数是避免被零除的最小值。如果您计算的数字至少大于 100 倍,那么您应该没问题

【讨论】:

    【解决方案3】:

    在 IEEE-754 中,您可以将浮点值作为符号整数值进行比较。所以我在这里使用它。整数比较类似于我在this answer 中使用的比较,但未签名版本。以下是转换为无分支之前的原始版本

    这里也没有像其他问题那样使用减号运算符,但您可以通过转换回使用减号来缩短代码。

    int isGreaterI(uint64_t x, uint64_t y)
    {
        if ((x ^ y) >> 63) // never happens as we only pass absolute values as below
        {
            return (x >> 63) & 1;
        }
        else
        {
            uint64_t xm   = ~x + 1ULL;
            uint64_t diff = y + xm;
            return diff >> 63;
        }
    }
    
    int isGreaterF(double a, double b)
    {
        uint64_t ai = *((uint64_t*)(&a));
        uint64_t bi = *((uint64_t*)(&b));
        int result;
        if ((ai ^ bi) >> 63) // different signs
        {
            result = bi >> 63; // bi < 0, ai > 0
        }
        else
        {
            uint64_t sign = ai >> 63;
            result = isGreaterI(ai, bi);
            if (sign)
                result = !result;
        }
        return result;
    }
    

    if 块中混合分支结果以删除条件并删除不必要的分支后,我们得到了以下最终版本

    int isGreaterMagnitude(uint64_t x, uint64_t y)
    {
        uint64_t xm = ~x + 1ULL;
        uint64_t diff = y + xm;
        return diff >> 63;
    }
    
    int isGreaterF(double a, double b)
    {
        uint64_t ai = *((uint64_t*)(&a));
        uint64_t bi = *((uint64_t*)(&b));
        uint64_t mask = ((ai ^ bi) >> 63) + (~0ULL); // mask = 0 if a and b have different signs
    
        uint64_t r1 = bi >> 63;
    
        uint64_t sign = (int64_t)ai >> 63;
        uint64_t r2 = isGreaterMagnitude(ai, bi);
        r2 = (r2 & ~sign) | ((1 - r2) & sign);
    
        return (r1 & ~mask) | (r2 & mask);
    }
    

    【讨论】:

      猜你喜欢
      • 2013-03-27
      • 2011-11-04
      • 1970-01-01
      • 2010-11-25
      • 1970-01-01
      • 2021-03-19
      • 1970-01-01
      • 1970-01-01
      • 2011-11-24
      相关资源
      最近更新 更多