Проверьте, является ли double равномерно делимым другим двойником в C?
Как проверить, является ли double x равномерно делимым другим двойным y в C? С целыми числами я бы просто использовал modulo, но что было бы правильным/лучшим способом сделать это с помощью парных?
Я знаю, что числа с плавающей запятой переносят с ними неточность, но я получаю двойной от стандартного ввода. Может быть, я не должен отсканировать его как двойник сразу, а вместо него вместо двух целых чисел, но откуда бы я пошел?
Ответы
Ответ 1
Стандартный заголовок 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
номеров x
и y
, и проверить, есть ли 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
& ne; 0, остаток r = x REM y
определяется независимо от режима округления по математическому соотношению r = x - ny
, где n
- целое, ближайшее к точному значению x/y
; всякий раз, когда | n - x/y | = 1/2
, то n
четно. Таким образом, остаток всегда точен. Если r = 0
, его знак должен быть знаком x
. Это определение применимо для всех реализаций.
(Либо fmod()
, либо remainder()
должен работать для вас.)
Ответ 2
Семейство функций fmod() дает ужасные результаты. Предположим, вы хотите определить, равно ли 42 делится на 0,4. Это 105 раз. Однако fmod выполняет разделение и получает результат, например, 104.99999, который затем округляется до 104, что приводит к остатку 0.399999, что дает ложно отрицательный результат. Остаток l(), однако, работает. Даже 0,4 сам по себе представлен неточно в плавающей точке.
Для людей, которые не понимают понятие "равномерно делимое", это не имеет никакого отношения к тому, что результат является четным числом - вы, вероятно, имеете свою этимологию назад. Даже числа - это те числа, которые равномерно делятся на 2. И понятие делимости полностью справедливо для нецелых чисел. Равномерное делимое означает, что результат деления является целым числом независимо от того, являются ли дивиденды или делители. Примером может служить металлический токарный станок с винтом с шагом 3 мм и резка болта с шагом 0,4 мм. 14 нитей на 3 мм выстраиваются в линию с 105 резьбами на 0,4 мм. Расчет делимости используется для указания, где различные движущиеся части токарного станка синхронизируются снова, чтобы вы могли повторно подключиться к следующему разрезанию. Другим примером являются имперские измерения, которые были преобразованы в метрику. 50,8 мм (2 ") равномерно делится на 25,4 мм (1 дюйм). Даже без метрических преобразований размеры часто являются нецелыми, а делимость часто является проблемой: 0,5" равномерно делится на 0,1 ", 0,125 дюйма и 0,250". Преобразование числа с плавающей запятой (например, 0,375 ") в дробное представление (3/8" ) является еще одним применением делимости к нецелочисленным числам.
Два альтернативных расчета в этой примерной функции дают одинаковые результаты для сотен различных пар чисел. Однако замена остатка l() на fmodl() или roundl() с помощью floorl() дает много недействительных результатов. Я изначально использовал пух 0,001. Фактическая ошибка вычисления обычно имеет порядок 1E-15, поэтому можно использовать меньший пух. Однако сравнение результата с 0.0 даст ложные отрицательные результаты. Вы можете выразить свой пух с точки зрения своего знаменателя, если вы работаете с очень маленькими числами. делимый (42, 0,4) и делимый (41,0.4) должен давать те же результаты, что и делимый (0,000000042, 0,0000000004) и делимый (0,000000041, 0,0000000004). И.Е. 42 нм и 41 нм делится на 0,4 нм? С версией функции, приведенной здесь, они это делают. С фиксированным пухом они не обязательно. Однако делимый (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);
}
Ответ 3
Я не уверен, что вы пытаетесь сделать, но я использовал fmod() из math.h в коде синтеза звука, где мне нужны мои параметры, чтобы быть float или double, и мне нужно было получить модуль.
Ответ 4
- Сканируйте их как двойные и назовите их x1 и x2
- Найдите, что x1/x2 использует деление и называет его x3
- Найти x1 - (x2 * x3) и посмотреть, достаточно ли это число близко к нулю - если оно тогда равно x делится на x2 (очевидно, учитывая здесь возможность отрицательных значений)
lol - строка 3 исправлена:)
Ответ 5
Если вы хотите быть абсолютно точным, вы можете использовать математику с фиксированной точкой. То есть, делайте все с помощью ints, но ints, которые (в вашем случае) обладают некоторой мощностью в 10 значений, которые они фактически представляют.
Скажите, что пользователь вводит 123.45 и 6789.1. Во-первых, вы хотите убедиться, что у вас одинаковое количество десятичных знаков, поэтому добавьте конечные нули в число с меньшим количеством знаков после запятой. Это дает нам 123.45 и 6789.10 (теперь оба с двумя знаками после запятой). Теперь просто удалите десятичную точку, чтобы получить 12345 и 678910. Если один делится на другой равномерно, то ваш ответ.
Это работает, потому что удаление десятичной точки умножается как на одну и ту же константу (100 в приведенном выше примере). (x * 100) / (y * 100) == x / y
Несколько вещей, о которых нужно заботиться: если вы читаете целую часть и дробную часть как ints, будьте осторожны, чтобы не потерять начальные нули в дробной части. (например: 0.1 и 0.0001 - это не одинаковое число!) Кроме того, если есть достаточное количество знаков после запятой, вы можете переполняться. Вы, вероятно, хотите, по крайней мере, использовать длинный.
Вы также можете выполнять вычисления с удвоениями, но это будет менее точно. Чтобы сделать это таким образом, выполните разделение, а затем сравните разницу между результатом и округленным результатом. Если в пределах некоторой небольшой допуск, то он делит равномерно.
Ответ 6
Как проверить, является ли double x равномерно распределяемым другим двойным y в C? С целыми числами я бы просто использовал modulo, но что было бы правильным/лучшим способом сделать это с помощью парных?
Вы включили бы и связали бы с математической библиотекой:
#include <math.h>
Затем вы вызываете функцию модуля с плавающей запятой fmod:
if (fmod(5.0, 2.5) == 0.0)
// evenly divisible
else
// not evenly divisible
Вы можете сравнить результат fmod с небольшим значением вместо 0.0 в зависимости от ваших потребностей.
Ответ 7
Понятие "четное число" определяется только для целых чисел. Вы не можете применить его к удвоению; это не имеет математического смысла. Из Wikipedia:
Четное число - это целое число, которое "равномерно делимый" на 2, т.е. делится на 2 без остатка.
Я предлагаю вам преобразовать двойники в ints, применяя любой метод, который вы решили (округление, усечение), а затем используйте modulo, как вы предлагаете.