C ++这应该更容易吗?

时间:2011-03-31 05:20:40

标签: c++ algorithm

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

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;
}

3 个答案:

答案 0 :(得分:10)

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

我鼓励大卫戈德伯格阅读这篇文章:What Every Computer Scientist Should Know About Floating Point Arithmetic。这有点啰嗦,所以您可能会喜欢这个网站:The Floating-Point Guide

事实是floor(num) == num是一段奇怪的代码。

  • numdouble
  • floor(num)返回double,接近int

麻烦的是,这并没有检查你真正想要的东西。例如,假设(为了示例)5无法完全表示为double,因此,计算机将存储5而不是存储4.999999999999

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

assert(num != floored);

通常,对于浮点数,精确比较无意义,因为舍入误差。

如果您坚持使用floor,我建议使用floor(num + 0.5)哪个更好,但略有偏见。更好的舍入方法是Banker's rounding,因为它是无偏见的,如果您愿意,文章会引用其他方法。请注意,银行家的四舍五入是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) { }

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

第二次尝试:

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

double mod = fmod(num, divisor);

if (mod <= epsilon) { }

更好,但不完全:modepsilon已签名! 是的,这是一个奇怪的模数,mod的符号是num的标志

第三次尝试:

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

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

if (mod <= eps) { }

好多了。

如果divisor来自整数,也应该能够很好地工作,因为不存在精确问题......或者至少不会太多。

编辑:第四次尝试,来自@ybungalobill

之前的尝试不能很好地处理错误的num/divisor错误的情况。像1.999/1.000 - &gt; 0.999,它差不多divisor所以我们应该表明平等,但它失败了。

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

if (mod <= 0.001 || fabs(1 - mod) <= 0.001) { }

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


但仍然有麻烦的原因。

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

Integer n = 12345678901234567890;
double d = n; // 1.234567890123457 * 10^20

截断表示无法将其映射回原始值。这不会导致doubleint出现任何问题,例如我的平台double上有8个字节而int是4个字节,因此可以使用,但更改{ {1}}到doublefloatint可能违反了这个假设,哦,地狱!

顺便说一句,你确定你真的需要浮点吗?

答案 1 :(得分:3)

根据以上评论,我相信你可以做到这一点......

double num (//whatever);
int divisor (//an integer);
if(fmod(num, divisor) == 0) {
         //do stuff;
}

答案 2 :(得分:0)

我没有检查过,但为什么不这样做呢?

if (floor(num) == num && !(static_cast<int>(num) % divisor)) {
    // do stuff...
}