Проверьте, делится ли двойное число равномерно на другое двойное число в C? - PullRequest
16 голосов
/ 18 января 2010

Как я могу проверить, делится ли двойное число x на другое двойное число y в C?С целыми числами я бы просто использовал модуль, но какой будет правильный / лучший способ сделать это с двойными числами?

Я знаю, что числа с плавающей запятой несут с собой неточность, но я получаю двойное от стандартного ввода.Может быть, я не должен сканировать это как двойное сразу, а как два целых числа, но куда мне тогда пойти?

Ответы [ 7 ]

17 голосов
/ 18 января 2010

Стандартный заголовок 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() должны работать на вас.)

7 голосов
/ 12 февраля 2010

Семейство функций fmod () дает ужасные результаты.Предположим, вы хотите определить, делится ли 42 на 0,4.Это 105 раз.Тем не менее, fmod выполняет деление и получает результат, подобный 104,9999, который затем округляется до 104, что дает остаток 0,399999, который дает ложноотрицательный результат.remainderl (), однако, похоже, работает.Даже само число 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") является еще одним применением делимости к нецелым числам.

Два альтернативных вычисления в этой функции примера дают одинаковыерезультаты для сотен различных пар чисел.Однако замена remainderl () на 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 голосов
/ 18 января 2010

Если вы хотите быть абсолютно точным, вы можете использовать математику с фиксированной точкой. То есть делайте все с целыми числами, но с целыми, которые (в вашем случае) имеют некоторую степень 10 от значения, которое они на самом деле представляют.

Скажем, пользователь вводит 123.45 и 6789.1. Во-первых, вы хотите убедиться, что у вас одинаковое количество десятичных знаков, поэтому добавьте конечные нули к одному с меньшим количеством десятичных знаков. Это дает нам 123,45 и 6789,10 (теперь оба с двумя десятичными знаками). Теперь просто удалите десятичную точку, чтобы получить 12345 и 678910. Если одно делится на другое равномерно, это ваш ответ.

Это работает, потому что удаление десятичной точки умножает оба на одну и ту же константу (100 в приведенном выше примере). (x * 100) / (y * 100) == x / y

Несколько вещей, которые следует соблюдать осторожно: если вы читаете целую и дробную части как целые, будьте осторожны, чтобы не потерять начальные нули на дробной части. (Например: 0,1 и 0,0001 не совпадают!) Кроме того, если есть достаточно десятичных разрядов, вы можете переполнить. Вы, вероятно, хотите, по крайней мере, использовать длинные.

Вы также можете выполнить вычисления с двойными числами, но это будет менее точно. Для этого выполните деление, а затем сравните разницу между результатом и округленным результатом. Если в пределах небольшого допуска, то он делится равномерно.

3 голосов
/ 18 января 2010

Я не уверен, что вы пытаетесь сделать, но я использовал fmod () из math.h в коде синтеза звука, где мне нужно, чтобы мои параметры были плавающими или удваивающимися, и мне нужно было получить модуль.

2 голосов
/ 18 января 2010

Как я могу проверить, делится ли двойной x поровну на другой двойной y в C? С целыми числами я бы просто использовал модуль, но какой был бы правильный / лучший способ сделать это с двойными числами?

Вы бы включили ссылку на математическую библиотеку:

#include <math.h>

Тогда вы бы вызвали функцию модуля с плавающей запятой fmod :

if (fmod(5.0, 2.5) == 0.0)
  // evenly divisible
else
  // not evenly divisible

Вы можете сравнить результат fmod с небольшим значением вместо 0,0 в зависимости от ваших потребностей.

2 голосов
/ 18 января 2010
  1. Отсканируйте их как двойники и назовите их x1 и x2
  2. Найдите, что x1 / x2 использует деление, и назовите его x3
  3. Найдите x1 - (x2 * x3) и посмотрите, достаточно ли это число близко к нулю - если это так, то x1 равномерно делится на x2 - (очевидно, учитывая возможность отрицательных значений здесь)

lol - строка 3 исправлена:)

0 голосов
/ 18 января 2010

Понятие "четное число" определено только для целых чисел.Вы не можете применить это к двойникам;это не имеет математического смысла.From Wikipedia :

Четным числом является целое число, которое «равномерно делится» на 2, т. Е. Делится на 2 без остатка.

Я предлагаю вам преобразовать ваши двойные значения в целые, применяя любой метод, который вы решите (округление, усечение), а затем использовать модуль по вашему усмотрению.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...