Чтобы округлить 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))
Если num
имеет значение, не близкое к диапазону int
, приведение (int)
приводит к неопределенному поведению.
Когда 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
}
Немного проверено - сделаю это позже, когда у меня будет время.