IEEE 754 рекомендует подход «округление от половины до четности»: если дробная часть d
равна 0,5, то округляется до ближайшего четного целого числа.Проблема заключается в том, что округление дробной части 0,5 в одном направлении приводит к смещению результатов;таким образом, вы должны округлить дробные 0,5 до половины и до половины времени, следовательно, бит «округление до ближайшего четного целого», округление до ближайшего нечетного числа также будет работать, как если бы подбрасывать справедливую монету, чтобы определить, какой путьgo.
Я думаю, что-то вроде этого было бы IEEE-правильным:
#include <math.h>
int is_even(double d) {
double int_part;
modf(d / 2.0, &int_part);
return 2.0 * int_part == d;
}
double round_ieee_754(double d) {
double i = floor(d);
d -= i;
if(d < 0.5)
return i;
if(d > 0.5)
return i + 1.0;
if(is_even(i))
return i;
return i + 1.0;
}
И это должно быть C99-ish (что указывает на то, что числа с дробными частями 0,5 должныбыть округленным от нуля):
#include <math.h>
double round_c99(double x) {
return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
}
И более компактная версия моей первой round_c99()
, эта лучше справляется с пересечением 56-битной границы мантиссы, не полагаясь на то, что x+0.5
или x-0.5
разумнычто нужно сделать:
#include <math.h>
double round_c99(double d) {
double int_part, frac_part;
frac_part = modf(d, &int_part);
if(fabs(frac_part) < 0.5)
return int_part;
return int_part > 0.0 ? int_part + 1.0 : int_part - 1.0;
}
Это будет иметь проблемы, если |int_part| >> 1
, но округлять двойное с большим показателем бессмысленно.Я уверен, что во всех трех тоже есть NaN, но мой мазохизм имеет пределы, и числовое программирование действительно не мое дело.
В вычислениях с плавающей запятой достаточно места для мелких ошибок, поэтому лаконично может не быть лучшим требованием.
Еще лучшим решением было бы побить вашего поставщика компиляторов примерно по лицу и шее, пока они не обеспечат надлежащую математическую библиотеку.