12

我想检查浮点值是否“几乎”是 32 的倍数。例如 64.1“几乎”可以被 32 整除,63.9 也是如此。

现在我正在这样做:

#define NEARLY_DIVISIBLE 0.1f
float offset = fmodf( val, 32.0f ) ;
if( offset < NEARLY_DIVISIBLE )
{
    // its near from above
}
// if it was 63.9, then the remainder would be large, so add some then and check again
else if( fmodf( val + 2*NEARLY_DIVISIBLE, 32.0f ) < NEARLY_DIVISIBLE )
{
    // its near from below
}

有更好的方法来做到这一点吗?

4

7 回答 7

3

好吧,您可以通过再减去 32 一次来从下面获取 mod 来删除第二个 fmodf。

  if( offset < NEARLY_DIVISIBLE )
   {
       // it's near from above
   }
   else if( offset-32.0f>-1*NEARLY_DIVISIBLE)
   {
       // it's near from below
   }
于 2010-03-22T04:18:33.800 回答
2

在符合标准的 C 实现中,可以使用remainder函数而不是fmod

#define NEARLY_DIVISIBLE 0.1f
float offset = remainderf(val, 32.0f);
if (fabsf(offset) < NEARLY_DIVISIBLE) {
    // Stuff
}

如果一个在不兼容的平台上(例如 MSVC++),那么remainder很遗憾,它不可用。我认为在这种情况下,快速乘法的答案是相当合理的。

于 2010-03-22T04:33:34.327 回答
2

您提到您必须使用32测试近可分性。以下理论应该适用于针对 2 的幂的近可分性检验:

#define THRESHOLD 0.11
int nearly_divisible(float f) {
    // printf("    %f\n", (a - (float)((long) a)));
    register long l1, l2;
    l1 = (long) (f + THRESHOLD);
    l2 = (long) f;
    return !(l1 & 31) && (l2 & 31 ? 1 : f - (float) l2 <= THRESHOLD);
}

我们正在做的是强制浮动,并且浮动 + 阈值长。

f       (long) f    (long) (f + THRESHOLD)
63.9    63          64
64      64          64
64.1    64          64

现在我们测试 (long) f 是否可以被 32 整除。只需检查低五位,如果它们都设置为零,则该数字可以被 32 整除。这会导致一系列误报:64.2 到 64.8,转换时来长,也都64了,一考就过。因此,我们检查它们的截断形式和 f 之间的差异是否小于或等于 THRESHOLD。

这也有一个问题:f - (float) l2 <= THRESHOLD 将适用于 64 和 64.1,但不适用于 63.9。因此,我们为小于 64 的数字添加了一个例外(当增加 THRESHOLD 并随后强制为 long 时——请注意,讨论中的测试必须包含第一个测试——可被 32 整除),通过指定低 5 位不为零。这将适用于 63 (1000000 - 1 == 1 11111 )。

这三个测试的组合将表明该数字是否可以被 32 整除。我希望这很清楚,请原谅我奇怪的英语。

我刚刚测试了其他三的幂的可扩展性——下面的程序打印了 383.5 和 388.4 之间的数字,这些数字可以被 128 整除。

#include <stdio.h>

#define THRESHOLD 0.11

int main(void) {
    int nearly_divisible(float);
    int i;
    float f = 383.5;
    for (i=0; i<50; i++) {
        printf("%6.1f %s\n", f, (nearly_divisible(f) ? "true" : "false"));
        f += 0.1;
    }
    return 0;
}

int nearly_divisible(float f) {
    // printf("    %f\n", (a - (float)((long) a)));
    register long l1, l2;
    l1 = (long) (f + THRESHOLD);
    l2 = (long) f;
    return !(l1 & 127) && (l2 & 127 ? 1 : f - (float) l2 <= THRESHOLD);
}

到目前为止似乎运作良好!

于 2010-03-22T09:30:47.587 回答
0

对于我收集的内容,您想检测一个数字是否几乎可以被其他数字整除,对吗?

我会做这样的事情:

#define NEARLY_DIVISIBLE 0.1f 

bool IsNearlyDivisible(float n1, float n2)
{
   float remainder = (fmodf(n1, n2) / n2);
   remainder = remainder < 0f   ? -remainder : remainder;
   remainder = remainder > 0.5f ? 1 - remainder   : remainder;
   return (remainder <= NEARLY_DIVISIBLE);
}
于 2010-03-22T05:07:44.490 回答
0

我认为是对的:

bool nearlyDivisible(float num,float div){
float f = num % div;
if(f>div/2.0f){
f=f-div;
}
f=f>0?f:0.0f-f;
return f<0.1f;
}
于 2010-03-22T05:23:46.383 回答
0

为什么不直接除以 32,然后四舍五入并取舍入后的数字与实际结果之间的差值?

类似的东西(原谅未经测试的/伪代码,没有时间查找):

#define NEARLY_DIVISIBLE 0.1f
float result = val / 32.0f;
float nearest_int = nearbyintf(result);
float difference = abs(result - nearest_int);
if( difference < NEARLY_DIVISIBLE )
{
    // It's nearly divisible
}

如果您仍想从上方和下方进行检查,您可以移除 abs,并检查差异是 >0 还是 <0。

于 2010-03-22T14:53:57.753 回答
0

这是没有使用 fmodf 两次。

int main(void)
{
    #define NEARLY_DIVISIBLE 0.1f
    #define DIVISOR 32.0f
    #define ARRAY_SIZE 4
    double test_var1[ARRAY_SIZE] = {63.9,64.1,65,63.8};
    int i = 54;
    double rest;
    for(i=0;i<ARRAY_SIZE;i++)
    {
        rest = fmod(test_var1[i] ,DIVISOR);
        if(rest < NEARLY_DIVISIBLE)
        {
            printf("Number %f max %f larger than  a factor of the divisor:%f\n",test_var1[i],NEARLY_DIVISIBLE,DIVISOR);
        }
        else if( -(rest-DIVISOR) < NEARLY_DIVISIBLE)
        {
            printf("Number %f max %f less than  a factor of the divisor:%f\n",test_var1[i],NEARLY_DIVISIBLE,DIVISOR);
        }
    }
    return 0;
}
于 2010-03-22T21:11:43.917 回答