我在考虑floor
中提供的math.h
功能。它很容易使用:
#include <stdio.h>
#include <math.h>
int main(void)
{
for (double a = 12.5; a < 13.4; a += 0.1)
printf("floor of %.1lf is %.1lf\n", a, floor(a));
return 0;
}
如果我想编写自己的实现怎么办?它看起来会像这样:
#include <stdio.h>
#include <math.h>
double my_floor(double num)
{
return (int)num;
}
int main(void)
{
double a;
for (a = 12.5; a < 13.4; a += 0.1)
printf("floor of %.1lf is %.1lf\n", a, floor(a));
printf("\n\n");
for (a = 12.5; a < 13.4; a += 0.1)
printf("floor of %.1lf is %.1lf\n", a, my_floor(a));
return 0;
}
似乎它不适用于负数(my_floor
),但第二个似乎没问题(my_floor_2
):
#include <stdio.h>
#include <math.h>
double my_floor(double num)
{
return (int)num;
}
double my_floor_2(double num)
{
if(num < 0)
return (int)num - 1;
else
return (int)num;
}
int main(void)
{
double a1 = -12.5;
printf("%lf\n", floor(a1));
printf("%lf\n", my_floor(a1));
printf("%lf\n", my_floor_2(a1));
return 0;
}
程序输出:
其中一个最终是否正确?
答案 0 :(得分:5)
不,你不能这样解决它。编写自己的实现的最佳方法是从平台上的C标准库中窃取一个。但请注意,可能包含特定于平台的细微差别,因此可能无法移植。
C标准库floor
函数通常很聪明,因为它不能通过转换为整数类型来工作。如果确实如此,那么您将面临signed
整数溢出的风险,其行为未定义。 (请注意,int
的最小可能范围是-32767到+32767。
精确实现还取决于平台上使用的浮点方案。
对于使用IEEE754浮点和long long
类型的平台,您可以采用此方案:
答案 1 :(得分:2)
你的两次尝试都有局限性:
double
值超出int
类型的范围,则转换为int
是实现定义的。double
值为负但为整数,则返回(int)num - 1
不正确。这是一个(几乎)可移植版本,试图处理所有情况:
double my_floor_2(double num) {
if (num >= LLONG_MAX || num <= LLONG_MIN || num != num) {
/* handle large values, infinities and nan */
return num;
}
long long n = (long long)num;
double d = (double)n;
if (d == num || num >= 0)
return d;
else
return d - 1;
}
如果类型long long
的值位数多于类型double
,则应该是正确的,大多数现代系统都是如此。
答案 2 :(得分:0)
在 C ++ 和32位算术中,可以这样做:
//---------------------------------------------------------------------------
// IEEE 754 double MSW masks
const DWORD _f64_sig =0x80000000; // sign
const DWORD _f64_exp =0x7FF00000; // exponent
const DWORD _f64_exp_sig=0x40000000; // exponent sign
const DWORD _f64_exp_bia=0x3FF00000; // exponent bias
const DWORD _f64_exp_lsb=0x00100000; // exponent LSB
const DWORD _f64_exp_pos= 20; // exponent LSB bit position
const DWORD _f64_man =0x000FFFFF; // mantisa
const DWORD _f64_man_msb=0x00080000; // mantisa MSB
const DWORD _f64_man_bits= 52; // mantisa bits
// IEEE 754 single masks
const DWORD _f32_sig =0x80000000; // sign
const DWORD _f32_exp =0x7F800000; // exponent
const DWORD _f32_exp_sig=0x40000000; // exponent sign
const DWORD _f32_exp_bia=0x3F800000; // exponent bias
const DWORD _f32_exp_lsb=0x00800000; // exponent LSB
const DWORD _f32_exp_pos= 23; // exponent LSB bit position
const DWORD _f32_man =0x007FFFFF; // mantisa
const DWORD _f32_man_msb=0x00400000; // mantisa MSB
const DWORD _f32_man_bits= 23; // mantisa bits
//---------------------------------------------------------------------------
double f64_floor(double x)
{
const int h=1; // may be platform dependent MSB/LSB order
const int l=0;
union _f64 // semi result
{
double f; // 64bit floating point
DWORD u[2]; // 2x32 bit uint
} y;
DWORD m,a;
int sig,exp,sh;
y.f=x;
// extract sign
sig =y.u[h]&_f64_sig;
// extract exponent
exp =((y.u[h]&_f64_exp)>>_f64_exp_pos)-(_f64_exp_bia>>_f64_exp_pos);
// floor bit shift
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;
}
// LSW
if (sh>0)
{
if (sh<32) m=(0xFFFFFFFF>>sh)<<sh; else m=0;
a=y.u[l]&(m^0xFFFFFFFF); y.u[l]&=m;
}
// MSW
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 // semi result
{
float f; // 32bit floating point
DWORD u; // 32 bit uint
} y;
DWORD m,a;
int sig,exp,sh;
y.f=x;
// extract sign
sig =y.u&_f32_sig;
// extract exponent
exp =((y.u&_f32_exp)>>_f32_exp_pos)-(_f32_exp_bia>>_f32_exp_pos);
// floor bit shift
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;
}
//---------------------------------------------------------------------------
重点是制作将从尾数中清除十进制位的掩码,如果是负输入,则非零清零位会减少结果。要访问各个位,您可以使用union将浮点值转换为整数表示(如示例中所示)或使用指针代替。
我在简单的 VCL 应用中对此进行了测试,如下所示:
float f32;
double f64;
AnsiString txt="";
// 64 bit
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); }
}
// 32 bit
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 之外拍摄,则只需更改{{1到你手头的任何字符串类型,并将输出从AnsiString
更改为你得到的任何东西(如控制台TMemo::mm_log
或其他)
如果出现差异,cout
的双重调用是为了进行调试(您可以直接放置断点并单步处理错误情况)。
<强> [注释] 强>
请注意单词的顺序( MSW,LSW )与平台有关,因此您应相应地调整fxx_floor()
常量。此代码未经过优化,因此很容易理解,因此不要指望它会很快。
答案 3 :(得分:0)
尽管其他人已经说过您需要从C标准库中获取它,但我已经构建了一个floor
函数并在tio.run (Try It Online)和onlinegdb.com上对其进行了测试。该函数本身不需要任何#include
文件,但是为了打印出答案,我添加了stdio.h
(在tio.run和onlinegdb.com中,不在此处)。在这里:
long double myFloor(long double x) /* Change this to your liking: long double might
be float in your situation. */
{
long double xcopy=x<0?x*-1:x;
unsigned int zeros=0;
long double n=1;
for(n=1;xcopy>n*10;n*=10,++zeros);
for(xcopy-=n;zeros!=-1;xcopy-=n)
if(xcopy<0)
{
xcopy+=n;
n/=10;
--zeros;
}
xcopy+=n;
return x<0?(xcopy==0?x:x-(1-xcopy)):(x-xcopy);
}
浮点数的下限是小于或等于它的最大整数。以下是一些示例:
floor(5.7) = 5
floor(3) = 3
floor(9.9) = 9
floor(7.0) = 7
floor(-7.9) = -8
floor(-5.0) = -5
floor(-3.3) = -3
floor(0) = 0
floor(-0.0) = -0
floor(-0) = -0
答案 4 :(得分:0)
与宽整数类型相比,浮点类型的精度足够小时,如果浮点值在整数范围内,则强制转换为该整数类型。
请查看该函数以获取long long
范围之外的值,NAN,无穷大和-0.0所需的沙子调整。
#if DBL_MANT_DIG >= 64
#error TBD code
#endif
// LLONG_MAX is not exact as a double, yet LLONG_MAX + 1 is
#define LLONG_MAX_P1 ((LLONG_MAX/2 + 1)*2.0)
double my_floor(double x) {
if (x >= 0.0) {
if (x < LLONG_MAX_P1) {
return (double)(long long)x;
}
return x;
} else if (x < 0.0) {
if (x >= LLONG_MIN) {
long long ix = (long long) x;
return (ix == x) ? x : (double)(ix-1);
}
return x;
}
return x; // NAN
}