检查双倍是否可以被C中的另一个双重整除?

est*_*tan 16 c floating-point

如何检查双x是否可以被C中的另一个双y整除?使用整数我只会使用模数,但是用双精度执行它的正确/最佳方法是什么?

我知道浮点数带有不精确性,但我从标准输入得到了双倍.也许我不应该直接扫描它,而是两个整数,但是从那时起我会去哪里?

Alo*_*hal 17

标准头math.h定义了以下功能:

  • double fmod(double x, double y);
  • float fmodf(float x, float y);
  • long double fmodl(long double x, long double y);

这些函数返回x除以的余数的结果y.结果与符号相同x.您可以使用r = fmod(x, y);double数字xy,并检查r == 0.如果你不想测试精确的可分性但增加一些容差,那么你可以检查是否r"足够接近"0或y(感谢caf).

fmodf()并且fmodl()是C99的新功能.

编辑:C99还定义了一个单独的remainder(double x, double y)函数,返回其余部分x/y.来自http://docs.sun.com/source/806-3568/ncg_lib.html:

remainder(x,y)是IEEE标准754-1985中规定的操作.之间的区别remainder(x,y),并fmod(x,y)是由返回结果的符号remainder(x,y)可能无法与任一方向的一致性x或者y,而fmod(x,y)总是返回其符号与一致的结果x.两个函数都返回精确的结果,并且不会生成不精确的异常.

...

y≠0时,r = x REM y无论舍入模式如何都通过数学关系定义余数r = x - ny,其中n最接近精确值的整数x/y; 无论何时| n - x/y | = 1/2,然后n是均匀的.因此,其余部分总是精确的.如果r = 0,它的标志应该是x.该定义适用于所有实现.

(要么fmod()remainder()应该为你工作.)

  • +1这就是为什么`fmod`函数存在的原因。但是,不需要公差-正确地执行`fmod`总是正确的。 (2认同)
  • 你还需要检查`r`是否与'y`"足够接近" - 例如,尝试`fmod(0.3,0.1)`. (2认同)

小智 8

fmod()函数系列产生了可怕的结果.假设您要确定42是否可被0.4整除.这是105次.但是,fmod进行除法并得到像104.99999这样的结果,然后向下舍入到104,得到0.399999的余数,这给出了假阴性结果.然而,remainderl()似乎有效.甚至0.4本身也表示不精确浮点.

对于那些没有理解"可分割"概念的人来说,它与结果是偶数无关 - 你可能有你的词源倒退.偶数是那些可被2整除的数.并且可分解的概念对于非整数是完全有效的.均匀可分,意味着无论被除数还是除数,除法的结果都是整数.一个示例应用是如果您有一个带有3mm螺距导螺杆的金属车床并且正在切割0.4mm螺距螺栓.3毫米的14个螺纹与0.4毫米的105个螺纹对齐.可分性计算用于指示车床的各个运动部件再次同步,以便您可以重新接通下一个切割通道.另一个例子是已经转换为公制的英制测量.50.8毫米(2英寸)可被25.4毫米(1英寸)整除.即使没有度量转换,维度通常也是非整数,但可分性通常是一个问题:0.5"可被0.1",0.125"和0.250"整除.将浮点数(例如0.375")转换为小数表示(3/8")是对非整数的可分性的又一个应用.

此示例函数中的两个备选计算为数百个不同的数字对提供相同的结果.但是,用floorl()替换带有fmodl()或roundl()的remainderl()会产生大量无效结果.我最初使用0.001的模糊.实际计算误差通常为1E-15阶,因此可以使用较小的模糊.但是,将结果与0.0进行比较将得出假阴性结果.如果你使用非常小的数字,你可能想用你的分母来表达你的模糊.divisible(42,0.4)和divisible(41,0.4)应该给出与divisible(0.000000042,0.0000000004)和divisible(0.000000041,0.0000000004)相同的结果.IE是42nm和41nm可被0.4nm整除?使用此处给出的函数版本,它们可以.固定的模糊,他们不一定.但是,可分(42,0.0000000004)仍然给出假阴性(误差为1.53003e-15,大于4E-19的模糊),因此比较相差9个数量级的数字是不可靠的.IEEE浮点有其局限性.注意我使用长双精度计算来最小化计算和表示错误.此功能未使用负数进行测试.

int divisible(long double a, long double b) 
{
  int result;
#if 1
   if(fabsl(((roundl(a/b)*b)- a)) <= (1E-9*b) ) {
    result=TRUE;
  } else {
    result=FALSE;
  }
#else
  if( fabsl(remainderl(a,b)) <= (1E-9*b ) ){
    result=TRUE;
  } else {
    result=FALSE;
  }
#endif
  // printf("divisible(%Lg, %Lg): %Lg, %Lg,%d\n", a, b, roundl(a/b), fabsl(((roundl(a/b)*b)-a)), result);
  return(result);
}
Run Code Online (Sandbox Code Playgroud)