Проверьте, является ли 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, как вы предлагаете.