Разложить число с плавающей точкой на его целую и дробную часть - PullRequest
0 голосов
/ 02 февраля 2020

Я реализую алгоритм линии дробной задержки. Одной из задач является разложение значения с плавающей точкой на его целую и дробную части. Я знаю, что есть много сообщений об этой топике c на SO, и я, вероятно, прочитал большинство из них. Однако я не нашел ни одного поста, посвященного специфике этого сценария.

  • Алгоритм должен использовать 64-битные значения с плавающей точкой.

  • Входные значения с плавающей запятой гарантированно всегда будут положительными. (время задержки не может быть отрицательным)

  • Выходная целочисленная часть должна быть представлена ​​целочисленным типом данных.

  • Целочисленного типа данных должно быть достаточно биты, так что преобразование из двойного в целое происходит без риска переполнения.

  • Следует избегать проблем, возникающих из-за того, что значения с плавающей запятой не имеют точного внутреннего представления. (т. е. 9223372036854775809.0 может быть внутренне представлен как 9223372036854775808.9999998 и при приведении к целочисленному типу ошибочно становится 9223372036854775808)

  • Реализация должна работать независимо от режима округления или настроек оптимизации компилятора.

Ответы [ 2 ]

1 голос
/ 03 февраля 2020

Учитывая, что максимальное значение целочисленной части входного значения с плавающей точкой x равно 2 63 -1, и что x неотрицательно, тогда оба:

double my_modf(double x, int64_t *intPartOut)
{
    double y;
    double fracPart = modf(x, &y);
    *intPartOut = y;
    return fracPart;
}

и:

double my_modf(double x, int64_t *intPartOut)
{
    int64_t y = x;
    *intPartOut = y;
    return x - y;
}

будут правильно возвращать целую часть в intPartOut и дробную часть в возвращаемом значении независимо от режима округления.

G CC 9.2 для x86_64 лучше оптимизирует последнюю версию , как и Apple Clang 11.0.0.

llround не будет возвращать целочисленную часть по желанию, поскольку она округляется до ближайшего целого числа, а не чем усечение.

Проблемы с x, содержащими ошибки, не могут быть решены с помощью информации, представленной в вопросе. Процедуры, показанные выше, не имеют ошибок; они возвращают ровно целую и дробную части своего ввода.

1 голос
/ 02 февраля 2020

Обновленный ответ после прочтения вашего комментария ниже.

Если вы уже уверены, что значения находятся в пределах [0, 2 ^ 63-1], тогда простое приведение будет быстрее, чем llround(), поскольку эта функция может также проверять наличие переполнения (в моей системе это указано на странице руководства, однако стандарт C этого не требует).

На моем компьютере, например (Nehalem x86-64) приведение - это одна инструкция (cvttsd2si), а llround(), очевидно, больше, чем единица.

Могу ли я получить правильный результат при простом приведении (усечение) или безопаснее округлять ?

Зависит от того, что вы имеете в виду под словом «право». Если значение в double может быть правильно представлено int64_t, то вы обязательно получите точно такое же значение. Однако, если значение не может быть точно представлено double, то усечение автоматически выполняется при приведении. Если вы хотите округлить значение другим способом, это другая история, и вам придется использовать одну из ceil(), floor() или round().

Если вы также уверены, что никакие значения не будут +/- Infinity или NaN (и в этом случае вы можете использовать -Ofast), то ваша вторая реализация должна быть самой быстрой, если вы хотите усечение в то время как третий должен быть самым быстрым, если вы хотите floor() значение.

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