长期听众,第一次来电。我对编程比较陌生,并回顾了我为旧实验室编写的一些代码。有没有更简单的方法来判断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;
}
答案 0 :(得分:10)
问题很奇怪,检查也是如此。问题在于,谈论浮点数的可分性是没有意义的,因为浮点数用二进制表示得不精确,而且可分性大约是精确度。
我鼓励大卫戈德伯格阅读这篇文章:What Every Computer Scientist Should Know About Floating Point Arithmetic。这有点啰嗦,所以您可能会喜欢这个网站:The Floating-Point Guide。
事实是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);
通常,对于浮点数,精确比较无意义,因为舍入误差。
如果您坚持使用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) { }
更好,但不完全: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) { }
好多了。
如果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
此截断表示无法将其映射回原始值。这不会导致double
和int
出现任何问题,例如我的平台double
上有8个字节而int
是4个字节,因此可以使用,但更改{ {1}}到double
或float
到int
可能违反了这个假设,哦,地狱!
顺便说一句,你确定你真的需要浮点吗?
答案 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...
}