[英]How to get the correct floor of a floating-point division?
我想获得两个正浮点数相除的浮点下限。 特别是我追求的最大浮点数不大于除法下限的确切值。 股息可以很大,除数可以很小,但在我的应用程序中,除法中没有溢出或下溢的风险。
如果我这样做:
quotient = floor(dividend / divisor);
我的问题是,当商大于尾数的精度时,除法的结果始终是 integer,因此 FPU 将它舍入而不是铺平它,因为它处于舍入到最近或偶数模式; floor()
也什么都不做,因为它已经输入了 integer。 由于它是四舍五入的,有时结果会大于确切的地板,这不是我所追求的。
在除法期间更改 FPU 的舍入模式将是一个解决方案,但这不是一个选项,所以除此之外,我怎样才能获得正确的地板?
(相关: 如何正确设置浮点对总和)
我最终使用整数进行除法。 以下函数仅适用于 IEC-559 浮点数或双精度数:
#include <stdint.h>
#include <math.h>
#ifdef __GNUC__
#define int_fast128 __int128
// other compilers pending
#endif
double truncdiv(double a, double b)
{
int ae, be, re, sh, sh2;
int_fast64_t am, bm;
int_fast64_t rm;
am = 9007199254740992. * frexp(a, &ae);
bm = 9007199254740992. * frexp(b, &be);
sh = 52 + (am < bm); // add 1 if quotient is 1 bit short
re = ae - be - sh;
// Truncate the mantissa when the exponent is in range -52..0
sh2 = re >= 0 ? 0 : -re;
rm = re < -52 ? 0 : (((int_fast128)am << sh) / bm) >> sh2 << sh2;
return ldexp(rm, re);
}
请注意,此 function 不是为处理有符号零、NaN、无穷大、非正规、溢出或被零除而编写的。 它也是截断除法而不是下除法,即它向零舍入,而不是向负无穷大。 它需要 128 位 integer 类型,可能并非在所有平台上都可用。 对于单精度,它只需要一个 64 位 integer 类型,它得到更广泛的支持:
#include <stdint.h>
#include <math.h>
float truncdivf(float a, float b)
{
int ae, be, re, sh, sh2;
int_fast32_t am, bm;
int_fast32_t rm;
am = 16777216.f * frexpf(a, &ae);
bm = 16777216.f * frexpf(b, &be);
sh = 23 + (am < bm); // add 1 if quotient is 1 bit short
re = ae - be - sh;
// Truncate the mantissa when the exponent is in range -23..0
sh2 = re >= 0 ? 0 : -re;
rm = re < -23 ? 0 : (((int_fast64_t)am << sh) / bm) >> sh2 << sh2;
return ldexpf(rm, re);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.