Вопрос странный, и проверки тоже. Проблема в том, что не имеет смысла говорить о делимости числа с плавающей запятой, потому что число с плавающей запятой представляется неточно в двоичном виде, а делимость составляет около точность .
Я призываю вас прочитать эту статью Дэвида Голдберга: Что должен знать каждый компьютерный ученый об арифметике с плавающей запятой . Это немного затянуто, поэтому вы можете по достоинству оценить этот веб-сайт: Руководство с плавающей точкой .
Правда в том, что floor(num) == num
- странный кусок кода.
num
является double
floor(num)
возвращает double
, близкое к int
Беда в том, что это не проверяет, что вы действительно хотели. Например, предположим (ради примера), что 5
нельзя представить в точности как двойное число, поэтому вместо хранения 5
компьютер будет хранить 4.999999999999
.
double num = 5; // 4.999999999999999
double floored = floor(num); // 4.0
assert(num != floored);
В общем случае точные сравнения бессмысленны для чисел с плавающей запятой из-за ошибок округления.
Если вы настаиваете на использовании 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) { }
К сожалению, он не прошел один важный тест: величина 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
подписаны! Да, это странно по модулю, знак мод - это знак числа
Третья попытка:
// 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
-> 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 байтам, поэтому он будет работать, но при изменении double
на float
или int
до long
может нарушить это предположение, о черт!
Кстати, вы действительно нуждаетесь в плавающей запятой?