Я ищу альтернативу функциям ceil()
и floor()
в C, поскольку мне не разрешено использовать их в проекте.
То, что я построил до сих пор, представляет собой сложный путь вперед и назад с использованием оператора приведения и с этим преобразованием из значения с плавающей запятой (в моем случае double
) в int
и позже, поскольку мне нужны ближайшие целые числа, выше и ниже заданного значения с плавающей запятой, чтобы быть также double
значениями, обратно к double
:
#include <stdio.h>
int main(void) {
double original = 124.576;
double floorint;
double ceilint;
int f;
int c;
f = (int)original; //Truncation to closest floor integer value
c = f + 1;
floorint = (double)f;
ceilint = (double)c;
printf("Original Value: %lf, Floor Int: %lf , Ceil Int: %lf", original, floorint, ceilint);
}
Вывод:
Original Value: 124.576000, Floor Int: 124.000000 , Ceil Int: 125.000000
Для этого примера обычно мне не нужно, чтобы целочисленные значения ceil и floor c
и f
преобразовывались обратно в double
, но они мне нужны в double
в моей реальной программе. Считайте это требованием для задачи.
Хотя результат дает желаемые значения и пока кажется правильным, я все еще сомневаюсь, действительно ли этот метод настолько верен и уместен, или, говоря более ясно, если этот метод действительно вызывает какое-либо плохое поведение или проблему в программе или дает мне потерю производительности по сравнению с другими альтернативами, если есть другие возможные альтернативы.
Вы знаете лучшую альтернативу? И если да, то почему этот должен быть лучше?
Большое Вам спасибо.
_mm_round_sd
если серьезно — person RobertS supports Monica Cellio schedule 13.12.2019
@Sopel Что мне делать с _mm_round_sd
? — person RobertS supports Monica Cellio schedule 13.12.2019
@ RobertS-ReinstateMonica а что, если ввод 124 !!!! — person RobertS supports Monica Cellio schedule 13.12.2019
@ManthanTilva Ну, я думаю, вы правы, я должен предоставить условие, что значение ceil должно быть только + 1 от значения пола, если исходное значение является истинным значением с плавающей запятой. В противном случае значение потолка должно быть эквивалентно значению пола. Для значения пола в этом случае не должно быть существенной разницы. — person RobertS supports Monica Cellio schedule 13.12.2019
@ RobertS-ReinstateMonica: Совершенно верно. Вот что делает выражение 0.0 + i + (f != i)
в моем ответе. — person RobertS supports Monica Cellio schedule 13.12.2019
OP’code не работает:
original
— это уже целое число.original
— это негатив, как и-1.5
. Усечения здесь нет.original
находится за пределамиint
диапазона.original
— это не число.Альтернативное строительство
double my_ceil(double x)
Использование приведения к некоторому трюку с целочисленным типом является проблемой, когда
x
превышает размер целочисленного диапазона. Поэтому сначала проверьте, находится лиx
в диапазоне достаточно широкого целого числа (точность которого превышаетdouble
).x
значения за пределами, которые уже являются целыми числами. Рекомендую использовать самое широкое целое число(u)intmax_t
.Помните, что приведение к целому числу — это округление к 0, а не к этажу. Требуется другая обработка, если
x
отрицательное / положительное значение, когда кодceil()
илиfloor()
. Код OP пропустил это.Я бы избегал
if (x >= INTMAX_MAX) {
, поскольку это связано с(double) INTMAX_MAX
, округление и точное значение которого «выбираются способом, определяемым реализацией». Вместо этого я бы сравнил сINTMAX_MAX_P1
.some_integer_MAX
— это Число Мерсенна, а с дополнением до 2...MIN
является отрицательной «степенью двойки».Поскольку
x
может быть не числом, более полезно отменить сравнение, как реляционное сравнение NaN неверно.Большое спасибо за подробное объяснение. Да, это то, о чем я просил, так как я просто не знаю всех обстоятельств как новичок. Мне нужно время, чтобы пройти отдельные шаги и разобраться в них в деталях. Большое спасибо, chux! — person RobertS supports Monica Cellio; 14.12.2019
@chqrlie Да, так лучше. — person RobertS supports Monica Cellio; 14.12.2019
Вы упускаете важный шаг: вам нужно проверить, является ли число уже целым, поэтому для
ceil
, предполагающего неотрицательные числа (обобщение тривиально), используйте что-то вродеЕще один недостающий элемент в головоломке, который закрывается заключенным мной
if
, — это проверить, находится лиf
в пределахlong long
. На обычных платформах, еслиf
находился за пределамиlong long
, то он в любом случае был бы интегральным.Обратите внимание, что
floor
тривиально из-за того, что усечение доlong long
всегда приближается к нулю.Во-первых, большое спасибо за ваш ответ. Почему эти две вещи так важны для дела? Почему плохо, если я их опускаю? Для меня это не так ясно. — person RobertS supports Monica Cellio; 13.12.2019
~ @ RobertS-ReinstateMonica: Версия, которую я представляю, подходит для 64-битных операций с плавающей запятой IEEE754 с двойной точностью. — person RobertS supports Monica Cellio; 13.12.2019
Также существует загадка того, как обрабатывать NaN и бесконечности. — person RobertS supports Monica Cellio; 13.12.2019
@ th33lf: Конечно. Написание портативного
ceil
— это боль. Стандартная библиотека C проста в том, что они могут игнорировать переносимость. — person RobertS supports Monica Cellio; 13.12.2019@Bathsheba Какая польза от использования
long long int
вместо обычногоint
здесь в таком случае? — person RobertS supports Monica Cellio; 13.12.2019@Bathsheba Также: похоже, ваш ответ касается только потолка. Есть ли что-нибудь, что можно улучшить в технике напольного покрытия, как в моем примере? Я не очень доверяю себе, или это неправильные опасения? — person RobertS supports Monica Cellio; 13.12.2019
@ RobertS-ReinstateMonica: Полы более тривиальны, поскольку усечение до
long long
всегда приближается к нулю. — person RobertS supports Monica Cellio; 13.12.2019Но вам все равно следует изменить приведенный выше тест на if (f ‹= — LLONG_MIN || f› = LLONG_MAX) {…. — person RobertS supports Monica Cellio; 13.12.2019
@HansOlsson: работать с отрицательными числами для
ceil
не так просто, потому что формула0.0 + i + (f != i)
работает только дляf >= 0
. Дляf < 0
нужно просто использовать(double)i
— person RobertS supports Monica Cellio; 14.12.2019@chqrlie — Чтобы обработать это, также обновите оператор возврата, например, return 0.0 + i + (i ‹0? 0.0: (f! = i)); — или другой вариант. В настоящее время код просто говорит, что обрабатывать отрицательные числа тривиально, но не говорит, как это сделать. — person RobertS supports Monica Cellio; 16.12.2019