Альтернатива ceil () и floor () для получения ближайших целочисленных значений выше и ниже значения с плавающей запятой?

Я ищу альтернативу функциям 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

Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 2
  1. RobertS supports Monica Cellio

    Вы знаете лучшую альтернативу? И если да, то почему этот должен быть лучше?

    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 является отрицательной «степенью двойки».

    #include <inttypes.h>
    
    #define INTMAX_MAX_P1 ((INTMAX_MAX/2 + 1)*2.0)
    
    double my_ceil(double x) {
      if (x >= INTMAX_MAX_P1) {
        return x;
      }
      if (x < INTMAX_MIN) {
        return x;
      }
    
      intmax_t i = (intmax_t) x;      // this rounds towards 0
      if (i < 0 || x == i) return i;  // negative x is already rounded up.
      return i + 1.0;
    }
    

    Поскольку x может быть не числом, более полезно отменить сравнение, как реляционное сравнение NaN неверно.

    double my_ceil(double x) {
      if (x >= INTMAX_MIN && x < INTMAX_MAX_P1) {
        intmax_t i = (intmax_t) x;      // this rounds towards 0
        if (i < 0 || x == i) return i;  // negative x is already rounded up.
        return i + 1.0;
      }
      return x;
    }
    
    double my_floor(double x) {
      if (x >= INTMAX_MIN && x < INTMAX_MAX_P1) {
        intmax_t i = (intmax_t) x;      // this rounds towards 0
        if (i > 0 || x == i) return i;  // positive x is already rounded down.
        return i - 1.0;
      }
      return x;
    }
    

    Большое спасибо за подробное объяснение. Да, это то, о чем я просил, так как я просто не знаю всех обстоятельств как новичок. Мне нужно время, чтобы пройти отдельные шаги и разобраться в них в деталях. Большое спасибо, chux! person RobertS supports Monica Cellio; 14.12.2019

    @chqrlie Да, так лучше. person RobertS supports Monica Cellio; 14.12.2019

  2. RobertS supports Monica Cellio

    Вы упускаете важный шаг: вам нужно проверить, является ли число уже целым, поэтому для ceil, предполагающего неотрицательные числа (обобщение тривиально), используйте что-то вроде

    double ceil(double f){
        if (f >= LLONG_MAX){
            // f will be integral unless you have a really funky platform
            return f;
        } else {
            long long i = f;
            return 0.0 + i + (f != i); // to obviate potential long long overflow
        }
    }
    

    Еще один недостающий элемент в головоломке, который закрывается заключенным мной 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

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: