C++这应该更容易吗?

Seg*_*Gee 2 c++ algorithm

长期听众,第一次来电.我对编程比较陌生,并回顾了我为旧实验室编写的一些代码.有没有更简单的方法来判断双精度是否可被整数整除?

double num (//whatever);
int divisor (//an integer);
bool bananas;

   if(floor(num)!= num || static_cast<int>(num)%divisor != 0) {
     bananas=false;
   }   
   if(bananas==true)
         //do stuff;
}
Run Code Online (Sandbox Code Playgroud)

Mat*_* M. 10

问题很奇怪,检查也是如此.问题在于,谈论浮点数的可分性是没有意义的,因为浮点数用二进制表示得不精确,而且可分性是精确的.

我鼓励你阅读David Goldberg撰写的这篇文章:每个计算机科学家应该知道浮点运算.这有点啰嗦,所以你可能会喜欢这个网站,而不是:浮点指南.

事实是,这floor(num) == num是一段奇怪的代码.

  • num 是一个 double
  • floor(num)返回一个double,接近一个int

麻烦的是,这不会检查你真正想要的是什么.例如,假设(为了示例)5不能完全表示为double,因此,5计算机将存储而不是存储4.999999999999.

double num = 5; // 4.999999999999999
double floored = floor(num); // 4.0

assert(num != floored);
Run Code Online (Sandbox Code Playgroud)

通常,由于舍入误差,精确比较对于浮点数是没有意义的.

如果你坚持使用floor,我建议使用floor(num + 0.5)哪个更好,虽然略有偏见.更好的舍入方法是银行家的舍入,因为它是公正的,如果您愿意,文章会引用其他人.请注意,银行家的四舍五入是round......


至于你的问题,首先你需要一个有double意识的模数:fmod然后你需要记住避免精确比较位.

第一次(天真)尝试:

// divisor is deemed non-zero
// epsilon is a constant

double mod = fmod(num, divisor); // divisor will be converted to a double

if (mod <= epsilon) { }
Run Code Online (Sandbox Code Playgroud)

不幸的是,它失败了一个重要的测试:幅度mod取决于幅度divisor,因此如果divisor小于epsilon开始,它将永远是真的.

第二次尝试:

// divisor is deemed non-zero
double const epsilon = divisor / 1000.0;

double mod = fmod(num, divisor);

if (mod <= epsilon) { }
Run Code Online (Sandbox Code Playgroud)

更好,但不完全在那里:mod并且epsilon签了名!是的,这是一个奇怪的模数,mod的符号是num的符号

第三次尝试:

// divisor is deemed non-zero
double const eps = fabs(divisor / 1000.0);

double mod = fabs(fmod(num, divisor));

if (mod <= eps) { }
Run Code Online (Sandbox Code Playgroud)

好多了.

如果divisor来自整数,也应该工作得相当好,因为不存在精确问题......或者至少不会太多.

编辑:第四次尝试,由@ybungalobill

之前的尝试不能很好地解决num/divisor错误方面的错误.喜欢1.999/1.000- > 0.999,它几乎divisor是我们应该表明平等,但它失败了.

// divisor is deemed non-zero
mod = fabs(fmod(num/divisor, 1));

if (mod <= 0.001 || fabs(1 - mod) <= 0.001) { }
Run Code Online (Sandbox Code Playgroud)

看起来像一个永无止境的任务呃?


但仍有麻烦的原因.

double具有有限的精度,即可表示的有限位数(我认为16?).此精度可能不足以表示整数:

Integer n = 12345678901234567890;
double d = n; // 1.234567890123457 * 10^20
Run Code Online (Sandbox Code Playgroud)

这种截断意味着无法将其映射回原始值.这应该不会造成任何问题与doubleint,例如在我的平台double是8个字节,int4个字节,所以它的工作,但改变doublefloatintlong可能违反这一假设,哦,见鬼去吧!

顺便问一下,你确定你真的需要浮点数吗?