2010-04-03 3 views
6

Есть ли способ округлить числа в C?Как округлить числа с плавающей точкой до ближайшего целого числа в C?

Я не хочу использовать потолок и пол. Есть ли другая альтернатива?

Я наткнулся на этот фрагмент кода, когда я Googled для ответа:

(int)(num < 0 ? (num - 0.5) : (num + 0.5)) 

Эта строка всегда печатает значение как 4, даже если поплавок Num = 4.9.

+0

Существует много разных типов округления - какие (ы) вы хотите использовать? Пожалуйста, разместите примеры желаемого поведения. – 2010-04-03 10:24:43

+3

Я думаю, что проблема в другом месте, это должно обязательно напечатать 5 для ввода 4.9. – IVlad

+0

Да, преобразование типа с плавающей точкой в ​​целочисленный тип, который может представлять собой число требуемой подписи и величины, должно работать просто путем усечения десятичных знаков; этот код делает ± 0.5, чтобы заставить это усечение округлить исходное значение от нуля. – Arkku

ответ

10

4.9 + 0.5 - 5.4, который не может быть округленным до 4, если ваш компилятор серьезно не сломан.

Я только что подтвердил, что код Googled дает правильный ответ для 4.9.

[email protected]:~$ cat round.c 
#include <stdio.h> 

int main() { 
    float num = 4.9; 
    int n = (int)(num < 0 ? (num - 0.5) : (num + 0.5)); 
    printf("%d\n", n); 
} 
[email protected]:~$ make round && ./round 
cc  round.c -o round 
5 
[email protected]:~$ 
+0

Хммм .... Вы правы .... Утверждение может быть дополнительно упрощено до: int n = (num <0)? (num - 0,5): (num + 0,5); Я проверил это, и он работает безупречно. Не могли бы вы объяснить, как работает сравнение (num <0)? Я вставил точку останова в свою IDE и увидел, что проверка состояния (num <0) всегда указывает на FALSE, которая должна выполняться (num + 0.5) всегда – webgenius

+0

Не уверен, что вы просите, а выражение 'a? b: c' оценивает 'b', если' a' истинно и 'c' в противном случае. В этом случае идея состоит в том, чтобы переместить значение 'num' от нуля на' 0,5' до преобразования его в int путем обрезания десятичных знаков. Таким образом, усечение будет округлено до ближайшего int (например, 0.5 + 0.5 = 1.0 и 0.99 + 0.5 = 1.49 оба обрезаются до 1). – Arkku

+0

Но почему (num <0) сравнение сделано? num будет всегда 4.9 – webgenius

3

Общее решение заключается в использовании rint() и установить FLT_ROUNDS режим округления по мере необходимости.

-4

Try двигая скобки на ваше решение выше, так что он читает:

(int)(num < 0) ? (num - 0.5) : (num + 0.5) 

Использование Num в 4,9 она округляет до 5 на моей машине.

+1

Это не имеет никакого эффекта, кроме как, возможно, поднять предупреждение, когда вы присваиваете этому выражению значение int. Кроме того, '(int)' cast является избыточным, так как 'num <0' уже имеет тип' int'. –

+0

Что значит это, возможно, не работает. Это то же самое, что и ответ, который имеет 4 голоса. –

+2

Я не сказал, что это невозможно работать, я сказал, что это не имеет никакого эффекта, и я подразумевал, что удаление скобок, как вы это делали, ничего не меняет. –

1

Я думаю, что вы ищете это: int n = (d - floor(d) > 0.5) ? ceil(d) : floor(d);

+1

«Я не хочу использовать потолок и пол» ... – IVlad

+3

Если вопросник искренне не хочет использовать потолок и пол, но искренне счастлив использовать встроенное преобразование в int, тогда вопрос может быть подан под «изворотливыми вопросами в стиле интервью», которые подразумевают неестественное ограничение, чтобы проиллюстрировать некоторые моменты, полностью понятые только интервьюером ». К сожалению, это не соответствует тегу. –

+0

Стив, ты прав. Это вопрос, который часто задают в интервью. – webgenius

0

Вы можете быть в состоянии использовать fesetround() в fenv.h (введен в C99). Возможными аргументами являются макросы FE_DOWNWARD, FE_TONEAREST, FE_TOWARDZERO и FE_UPWARD, но обратите внимание, что не все из них обязательно определены - только те, которые поддерживаются платформой/реализацией. Затем вы можете использовать различные функции round, rint и nearbyint в math.h (также C99). Таким образом, вы можете установить желаемое поведение округления один раз и вызвать ту же функцию независимо от того, является ли это значение положительным или отрицательным.

(С, например, lround обычно нужно даже не установить округление направления для нормального использования, чтобы получить, как правило, то, что вы хотите.)

3

Я не уверен, что это хорошая идея. Этот код зависит от приведения, и я уверен, что точное усечение не определено.

float result = (num - floor(num) > 0.5) ? ceil(num) : floor(num); 

Я бы сказал, что это гораздо лучше (это в основном то, что писал Широко), так как он не зависит от каких-либо забросов.

1

работает с ошибкой. Идея этого заключается в том, что вы округлите, когда десятичное число меньше 0,5 и наоборот. (int) преобразует float в тип int, который просто сбрасывает десятичную. Если вы добавите .5 в положительное число, вы получите падение до следующего int. Если вы вычитаете .5 из отрицательного, то он делает то же самое.

0
int round(double x) 
{ 
return x >= 0.0 ? int(x + 0.5) : int(x - int(x-1) + 0.5) + int(x-1); 
} 

Это будет быстрее, чем версия с потолком и полом.

+0

Не решение C. 'int (x + 0,5)' недействителен C. Алгоритм завершается ошибкой для 'x чуть меньше 0,5,' x' не находится рядом с 'int'. – chux

0

Круглое значение х до точности p, где 0 < p < бесконечное. (f.ex. 0,25, 0.5, 1, 2, ...)

float RoundTo(float x, float p) 
{ 
    float y = 1/p; 
    return int((x+(1/(y+y)))*y)/y; 
} 

float RoundUp(float x, float p) 
{ 
    float y = 1/p; 
    return int((x+(1/y))*y)/y; 
} 

float RoundDown(float x, float p) 
{ 
    float y = 1/p; 
    return int(x*y)/y; 
} 
+0

То, что вы опубликовали, не компилируется на языке C - это сообщение, помеченное тегом. Возможно, вы кодируете на другой язык? – chux

+0

Кроме того, в C++ использование 'int (...)' fail должно 'x * (...)' превышать диапазон 'int'. – chux

+0

Это Arduino C/C++, извините, если это не «реально» C. Там, что int (float x) возвращает только целое число (удаление дескрипторов). Что такое «правильный» способ разделить только десятичные знаки? – JJussi

1

Чтобы округлить в С в float, есть 3 <math.h> функции для удовлетворения потребности. Рекомендовать rintf().

float nearbyintf(float x); 

В nearbyint функции вокруг своего аргумента целого значения в формате с плавающей точкой, используя текущее направление округления и без повышения „неточного“ «» исключения с плавающей точкой. C11dr §7.12.9.3 2

или

float rintf(float x); 

В rint функции отличаются от nearbyint функций (7.12.9.3) только в том, что rint функции могут поднимать '' неточное» «исключение с плавающей точкой», если результат отличается от значения аргументом. C11dr §7.12.9.4 2

или

float roundf(float x); 

В round функции круглого аргумента до ближайшего целого значения в формате с плавающей точкой, округление случаи на полпути от нуля, независимо от того, текущее направление округления. C11dr §7.12.9.6 2


Пример

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

void rtest(const char *fname, double (*f)(double x), double x) { 
    printf("Clear inexact flag  :%s\n", feclearexcept(FE_INEXACT) ? "Fail" : "Success"); 
    printf("Set round to nearest mode:%s\n", fesetround(FE_TONEAREST) ? "Fail" : "Success"); 

    double y = (*f)(x); 
    printf("%s(%f) --> %f\n", fname,x,y); 

    printf("Inexact flag    :%s\n", fetestexcept(FE_INEXACT) ? "Inexact" : "Exact"); 
    puts(""); 
} 

int main(void) { 
    double x = 8.5; 
    rtest("nearbyint", nearbyint, x); 
    rtest("rint", rint, x); 
    rtest("round", round, x); 
    return 0; 
} 

Выход

Clear inexact flag  :Success 
Set round to nearest mode:Success 
nearbyint(8.500000) --> 8.000000 
Inexact flag    :Exact 

Clear inexact flag  :Success 
Set round to nearest mode:Success 
rint(8.500000) --> 8.000000 
Inexact flag    :Inexact 

Clear inexact flag  :Success 
Set round to nearest mode:Success 
round(8.500000) --> 9.000000 
Inexact flag    :Exact 

Что слаб о коде OP в?

(int)(num < 0 ? (num - 0.5) : (num + 0.5)) 
  1. Должен num иметь значение не вблизи int диапазона, литые (int) результатов в непредсказуемом поведении.

  2. Когда num +/- 0.5 приводит к неточному ответу. Здесь маловероятно, что 0.5 является double, в результате чего добавление происходит с большей точностью, чем float. Когда num и 0.5 имеют одинаковую точность, добавление 0.5 к числу может привести к числовое округленное ответ. (Это не целое число округления позиции OP.) Пример: число, равное менее 0,5, должно округляться до 0 на цель OP, но num + 0.5 приводит к точному ответу между 1.0 и наименьшим double всего менее 1.0. Поскольку точный ответ не является представимым, эта сумма округляется, как правило, до 1.0, приводя к неправильному ответу. Аналогичная ситуация имеет место с большими числами.дилемма


OP о "Эта строка всегда печатает значение как 4, даже когда float num =4.9." не объяснимо, как указано. Требуется дополнительный код/​​информация. Я подозреваю, что ОП мог использовать int num = 4.9;.


// avoid all library calls 
// Relies on UINTMAX_MAX >= FLT_MAX_CONTINUOUS_INTEGER - 1 
float my_roundf(float x) { 
    // Test for large values of x 
    // All of the x values are whole numbers and need no rounding 
    #define FLT_MAX_CONTINUOUS_INTEGER (FLT_RADIX/FLT_EPSILON) 
    if (x >= FLT_MAX_CONTINUOUS_INTEGER) return x; 

    // Positive numbers 
    // Important: _no_ precision lost in the subtraction 
    // This is the key improvement over OP's method 
    if (x > 0) { 
    float floor_x = (float)(uintmax_t) x; 
    if (x - floor_x >= 0.5) floor_x += 1.0f; 
    return floor_x; 
    } 

    if (x < 0) return -my_roundf(-x); 
    return x; // x is 0.0, -0.0 or NaN 
} 

Испытано мало - будет делать это позже, когда у меня есть время.

+0

Было бы здорово иметь примеры установки текущего направления округления и неточного знака. –

+1

Добавлен пример @Ciro. – chux

Смежные вопросы