Как округлить числа с плавающей запятой до ближайшего целого числа в C? - PullRequest
7 голосов
/ 03 апреля 2010

Есть ли способ округлить числа в C?

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

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

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

Приведенная выше строка всегда печатает значение как 4, даже если число с плавающей запятой = 4.9.

Ответы [ 11 ]

9 голосов
/ 03 апреля 2010

4,9 + 0,5 равно 5,4, что не может быть округлено до 4, если ваш компилятор серьезно не работает.

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

marcelo@macbookpro-1:~$ 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);
}
marcelo@macbookpro-1:~$ make round && ./round
cc     round.c   -o round
5
marcelo@macbookpro-1:~$
3 голосов
/ 05 августа 2016

Чтобы округлить float в C, есть 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


* +1034 * Пример
#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 к числу может привести к числовому округленному ответу. (Это не округление целого числа в записи ОП.) Пример: число чуть меньше 0,5 должно округляться до 0 для каждой цели ОП, однако num + 0.5 дает точный ответ от 1,0 до наименьшего double, чуть менее 1,0 , Поскольку точный ответ не представим, эта сумма округляется, обычно до 1,0, что приводит к неправильному ответу. Аналогичная ситуация возникает с большими числами.

* * 1068

Дилемма OP о «Вышеприведенная строка всегда печатает значение как 4, даже когда float num =4.9». не объяснимо, как указано. Требуется дополнительный код / ​​информация. Я подозреваю, что OP, возможно, использовал 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
}

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

3 голосов
/ 03 апреля 2010

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

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

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

3 голосов
/ 03 апреля 2010

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

1 голос
/ 05 апреля 2010

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

1 голос
/ 03 апреля 2010

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

0 голосов
/ 08 августа 2018

просто добавьте 0.5 к числу и введите его .. и распечатайте его, напечатав его целым числом .. в противном случае вы можете использовать функцию round (), внутри которой просто передайте аргумент как соответствующее число.

0 голосов
/ 05 августа 2016

Округлить значение x с точностью до p, где 0

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 голосов
/ 13 мая 2010
int round(double x)
{
return x >= 0.0 ? int(x + 0.5) : int(x - int(x-1) + 0.5) + int(x-1);
}

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

0 голосов
/ 03 апреля 2010

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

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

...