在C++和32位算术中,例如可以这样做:
const DWORD _f64_sig =0x80000000;
const DWORD _f64_exp =0x7FF00000;
const DWORD _f64_exp_sig=0x40000000;
const DWORD _f64_exp_bia=0x3FF00000;
const DWORD _f64_exp_lsb=0x00100000;
const DWORD _f64_exp_pos= 20;
const DWORD _f64_man =0x000FFFFF;
const DWORD _f64_man_msb=0x00080000;
const DWORD _f64_man_bits= 52;
const DWORD _f32_sig =0x80000000;
const DWORD _f32_exp =0x7F800000;
const DWORD _f32_exp_sig=0x40000000;
const DWORD _f32_exp_bia=0x3F800000;
const DWORD _f32_exp_lsb=0x00800000;
const DWORD _f32_exp_pos= 23;
const DWORD _f32_man =0x007FFFFF;
const DWORD _f32_man_msb=0x00400000;
const DWORD _f32_man_bits= 23;
double f64_floor(double x)
{
const int h=1;
const int l=0;
union _f64
{
double f;
DWORD u[2];
} y;
DWORD m,a;
int sig,exp,sh;
y.f=x;
sig =y.u[h]&_f64_sig;
exp =((y.u[h]&_f64_exp)>>_f64_exp_pos)-(_f64_exp_bia>>_f64_exp_pos);
sh=_f64_man_bits-exp; a=0;
if (exp<0)
{
a=y.u[l]|(y.u[h]&_f64_man);
if (sig) return -1.0;
return 0.0;
}
if (sh>0)
{
if (sh<32) m=(0xFFFFFFFF>>sh)<<sh; else m=0;
a=y.u[l]&(m^0xFFFFFFFF); y.u[l]&=m;
}
sh-=32;
if (sh>0)
{
if (sh<_f64_exp_pos) m=(0xFFFFFFFF>>sh)<<sh; else m=_f64_sig|_f64_exp;
a|=y.u[h]&(m^0xFFFFFFFF); y.u[h]&=m;
}
if ((sig)&&(a)) y.f--;
return y.f;
}
float f32_floor(float x)
{
union
{
float f;
DWORD u;
} y;
DWORD m,a;
int sig,exp,sh;
y.f=x;
sig =y.u&_f32_sig;
exp =((y.u&_f32_exp)>>_f32_exp_pos)-(_f32_exp_bia>>_f32_exp_pos);
sh=_f32_man_bits-exp; a=0;
if (exp<0)
{
a=y.u&_f32_man;
if (sig) return -1.0;
return 0.0;
}
if (sh>0)
{
if (sh<_f32_exp_pos) m=(0xFFFFFFFF>>sh)<<sh; else m=_f32_sig|_f32_exp;
a|=y.u&(m^0xFFFFFFFF); y.u&=m;
}
if ((sig)&&(a)) y.f--;
return y.f;
}
重点是制作一个掩码,以清除尾数中的小数位,并在负输入和非零清除位的情况下将结果减少。要访问单个位,可以使用联合将浮点值转换为整数表示(如示例中所示),或者改用指针。
我在简单的VCL应用程序中进行了测试,如下所示:
float f32;
double f64;
AnsiString txt="";
txt+="[double]\r\n";
for (f64=-10.0;f64<=10.0;f64+=0.1)
if (fabs(floor(f64)-f64_floor(f64))>1e-6)
{
txt+=AnsiString().sprintf("%5.3lf %5.3lf %5.3lf\r\n",f64,floor(f64),f64_floor(f64));
f64_floor(f64);
}
for (f64=1;f64<=1e307;f64*=1.1)
{
if (fabs(floor( f64)-f64_floor( f64))>1e-6) { txt+=AnsiString().sprintf("%lf lf lf\r\n", f64,floor( f64),f64_floor( f64));
f64_floor( f64); }
if (fabs(floor(-f64)-f64_floor(-f64))>1e-6) { txt+=AnsiString().sprintf("%lf lf lf\r\n",-f64,floor(-f64),f64_floor(-f64));
f64_floor(-f64); }
}
txt+="[float]\r\n";
for (f32=-10.0;f32<=10.0;f32+=0.1)
if (fabs(floor(f32)-f32_floor(f32))>1e-6)
{
txt+=AnsiString().sprintf("%5.3lf %5.3lf %5.3lf\r\n",f32,floor(f32),f32_floor(f32));
f32_floor(f32);
}
for (f32=1;f32<=1e37;f32*=1.1)
{
if (fabs(floor( f32)-f32_floor( f32))>1e-6) { txt+=AnsiString().sprintf("%lf lf lf\r\n", f32,floor( f32),f32_floor( f32));
f32_floor( f32); }
if (fabs(floor(-f32)-f32_floor(-f32))>1e-6) { txt+=AnsiString().sprintf("%lf lf lf\r\n",-f32,floor(-f32),f32_floor(-f32));
f32_floor(-f32); }
}
mm_log->Lines->Add(txt);
没有任何差异的结果(因此在所有测试用例中,它与math.h
floor()
值匹配。如果您想在VCL之外尝试此操作,则只需将AnsiString
更改为您手头拥有的任何字符串类型,并将输出从TMemo::mm_log
更改为您拥有的任何内容(如控制台cout
或其他)
在存在差异的情况下双重调用fxx_floor()
是为了调试目的(您可以放置断点并直接进入错误案例)。
[注]
注意单词的顺序(MSW,LSW)取决于平台,因此您应相应地调整h,l
常量。此代码未经过优化,因此易于理解,因此不要指望它会很快。
floor(x)
返回小于或等于x的最大整数值
。如果您的函数已经这样做了,那么它现在是正确的。 - Scott Huntermy_floor_2(1e100)
函数,这将以某种方式告诉你它与原来的不同。 - WhatsUp