Создание обобщенной функции округления в C

Большинство вопросов округления по переполнению стека касаются конкретных случаев, и, поскольку я только что сделал общую функцию округления, я решил поделиться ею.


У меня есть число f, начальное значение a и приращение b, и я хочу "округлить" f до ближайшего элемента множества { a + bi | я целое число}. Например:

  • a знак равно 0.0, b знак равно 0.5: округлить до одного из значений 0.0, 0.5, 1.0, 1.5, так далее.

  • a знак равно 0.25, b знак равно 0.5: округлить до одного из значений 0.25, 0.75, 1.25, 1.75, так далее.

  • a знак равно 0.21, b знак равно 0.23: округлить до одного из значений 0.21, 0.44, 0.67, 0.80, так далее.

Стандартная библиотека C имеет roundf() но это только округляет до ближайшего целого числа.

Как мне это сделать?

1 ответ

Решение
#include <stdio.h>
#include <math.h>

float   round_float(float x, float inc, float start_val)
{
    return ( roundf( (x - start_val) / inc ) * inc + start_val );
}

int main(void)
{
    printf("%f\n", round_float(12.522, 0.5, 0));
    printf("%f\n", round_float(12.522, -0.5, -0.1));
    printf("%f\n", round_float(5.318, 0.23, 125));
    printf("%f\n", round_float(-12.522, 12, 5));
    printf("%f\n", round_float(-12.522, 3.6, -2));

}

выход:

12.500000
12.400000
5.400002
-7.000000
-12.799999

первый пример: round_float(10.1521, 0.5, 0)

Второй пример: round_float(10.1521, 0.5, 0.25)

последний пример: round_float(10.1521, 0.23, 0.21)


Расширенный пример

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

#include <stdio.h>
#include <math.h>

static inline float round_float(float x, float inc, float start_val)
{
    return roundf((x - start_val) / inc) * inc + start_val;
}

int main(void)
{
    printf("%f\n", round_float(12.522, 0.5, 0));
    printf("%f\n", round_float(12.522, -0.5, -0.1));
    printf("%f\n", round_float(5.318, 0.23, 125));
    printf("%f\n", round_float(-12.522, 12, 5));
    printf("%f\n", round_float(-12.522, 3.6, -2));

    static const float samples[] =
    {
        -14.2751, -12.3080, -10.5320,  -6.4804,  -1.0859,
          0.1999,   0.2099,   5.2980,   5.7819,  11.7052,
    };
    enum { NUM_SAMPLES = sizeof(samples) / sizeof(samples[0]) };

    static const float control[][2] =
    {
        { 0.00, 0.50 },
        { 0.25, 0.50 },
        { 0.21, 0.23 },
    };
    enum { NUM_CONTROL = sizeof(control) / sizeof(control[0]) };

    for (int i = 0; i < NUM_CONTROL; i++)
    {
        float a = control[i][0];
        float b = control[i][1];
        printf("Start: %8.4f; increment %8.4f\n", a, b);
        for (int j = 0; j < NUM_SAMPLES; j++)
        {
            printf("  Sample: %8.4f rounds to %8.4f\n",
                   samples[j], round_float(samples[j], b, a));
        }
    }

    return 0;
}

Пример вывода:

    12.500000
    12.400000
    5.400002
    -7.000000
    -12.799999
    Start:   0.0000; increment   0.5000
      Sample: -14.2751 rounds to -14.5000
      Sample: -12.3080 rounds to -12.5000
      Sample: -10.5320 rounds to -10.5000
      Sample:  -6.4804 rounds to  -6.5000
      Sample:  -1.0859 rounds to  -1.0000
      Sample:   0.1999 rounds to   0.0000
      Sample:   0.2099 rounds to   0.0000
      Sample:   5.2980 rounds to   5.5000
      Sample:   5.7819 rounds to   6.0000
      Sample:  11.7052 rounds to  11.5000
    Start:   0.2500; increment   0.5000
      Sample: -14.2751 rounds to -14.2500
      Sample: -12.3080 rounds to -12.2500
      Sample: -10.5320 rounds to -10.7500
      Sample:  -6.4804 rounds to  -6.2500
      Sample:  -1.0859 rounds to  -1.2500
      Sample:   0.1999 rounds to   0.2500
      Sample:   0.2099 rounds to   0.2500
      Sample:   5.2980 rounds to   5.2500
      Sample:   5.7819 rounds to   5.7500
      Sample:  11.7052 rounds to  11.7500
    Start:   0.2100; increment   0.2300
      Sample: -14.2751 rounds to -14.2800
      Sample: -12.3080 rounds to -12.2100
      Sample: -10.5320 rounds to -10.6000
      Sample:  -6.4804 rounds to  -6.4600
      Sample:  -1.0859 rounds to  -1.1700
      Sample:   0.1999 rounds to   0.2100
      Sample:   0.2099 rounds to   0.2100
      Sample:   5.2980 rounds to   5.2700
      Sample:   5.7819 rounds to   5.7300
      Sample:  11.7052 rounds to  11.7100

Я уверен, что более разумный выбор значений выборки сделает это еще яснее; это просто набор случайных чисел в диапазоне -20.. +20 в отсортированном порядке.

Извините за дополнительный уровень отступа в последнем примере. Предварительный просмотр не показывал "код", когда не было отступа в два уровня, и я понятия не имею, почему. (Конфигурация: Firefox Quantum 57.0.1 (ожидается перезапуск) в macOS High Sierra 10.13.2 и SO 2017.12.22.28257)

Другие вопросы по тегам