一、函数说明
对 double 而言,取整、取余的相关函数和运算符请参考下表:
VB6.0 | C | C# | 说 明 |
Int(x) | floor(x) | Math.Floor(x) | floor表示地板,也就是将向下取整数,即返回最大的整数使得 Math.Floor(1.9) 返回 1 Math.Floor(1.0) 返回 1 Math.Floor(-1.0) 返回 -1 Math.Floor(-1.3) 返回 -2 Math.Floor(-1.9) 返回 -2 |
ceil(x) | Math.Ceiling(x) | ceil表示天花板,也就是将向上取整数,即返回最小的整数使得 Math.Ceiling(1.9) 返回 2 Math.Ceiling(1.3) 返回 2 Math.Ceiling(1.0) 返回 1 Math.Ceiling(-1.0) 返回 -1 Math.Ceiling(-1.9) 返回 -1 | |
Fix(x) | Math.Truncate(x) | 表示取的整数部分 Math.Truncate(1.9) 返回 1 Math.Truncate(1.0) 返回 1 Math.Truncate(-1.0) 返回 -1 Math.Truncate(-1.9) 返回 -1 | |
Round(x) | Math.Round(x,...) | 表示四舍五入,如:四舍五入取整 Math.Round(1.9) 返回 2 Math.Round(1.5) 返回 2 Math.Round(1.3) 返回 1 Math.Round(1.0) 返回 1 Math.Round(-1.0) 返回 -1 Math.Round(-1.3) 返回 -1 Math.Round(-1.5) 返回 -2 Math.Round(-1.9) 返回 -2 | |
x Mod y | fmod(x,y) | x % y | 返回 x / y 的余数,其符号与 x 一致。 C语言里 % 只能用于整型变量 注意:VB6.0会对结果四舍五入取整 |
x / y | x / y | x / y | 返回 x / y,对于整数而言,将舍去小数部分。 |
1、详述Math.Round
Math.Round 还有更丰富的功能
①精确到小数点后n位
如:Math.Round(x,n) 表示对 x 只取小数点后 n 位,多余部分四舍五入。
②四舍五入问题
四舍是一定的,但对于五到底入还是不入呢?Math.Round是可以通过MidpointRounding来进行控制的。
MidpointRounding.AwayFromZero 表示五入,这是默认的方法,如:
Math.Round(1.5,MidpointRounding.AwayFromZero); 返回 2
Math.Round(-1.5,MidpointRounding.AwayFromZero); 返回 -2
MidpointRounding.ToEven 就比较有意思了,如果5前面的数字是奇数则入,否则就舍去。如:
Math.Round(2.5,MidpointRounding.ToEven); 返回 2
Math.Round(1.5,MidpointRounding.ToEven); 返回 2
Math.Round(-1.5,MidpointRounding.ToEven); 返回 -2
Math.Round(-2.5,MidpointRounding.ToEven); 返回 -2
二、函数关系
1、floor和ceil的关系
floor(x) = -ceil(-x),同样的ceil(x)=-floor(-x)。VB6.0里没有ceil函数,就可以用 -Int(-x) 来代替。
2、Math.Truncate 和floor、ceil的关系
Math.Truncate(x) =
写成C语言代码就是
double Truncate(double x)
{
if(x >= 0.0)
{
return floor(x);
}
return ceil(x); //或-floor(-x)
}
三、应用
1、归化角度
如:手表的秒针1分钟走1圈。分钟之后,它与起始位置的夹角是多少?显然这个角度等于度。现在要把这个角度归化到之间。即找到一个整数,使得成立。只考虑不等式的左半部分,则有,即。显然这个夹角应该是。
同样的,判断经度是否在经度和经度之间(只考虑劣弧,不考虑优弧),不能使用。如:经度180在经度-179和经度179之间,但它不满足-179 < 180 < 179。应该使用下式判断
函数Angle360(x)将返回角度(n是一个整数),且返回值在之间。显然,因此Angle360函数的C代码如下:
double Angle360(double x)
{
return x - floor(x / 360.0) * 360.0;
}
2、扩展fmod函数
上述问题里,将角度归化到之间可以使用fmod(360.0 * x,360.0);(C#里可以使用(360.0 * x) % 360.0)。但是,使用fmod感觉不太方便——它的返回值符号与第一个参数的符号相同。也就是说 x 小于零,则返回值将在之间。因此有必要扩展fmod函数。
fmod(x,y)的实质是找到合适的整数,使得,然后返回余数。
如果需要余数,则
如果需要余数,则
如果需要求绝对值最小的余数,则。
fmod函数的模拟代码如下
double fmod(double x,double y)
{
y = fabs(y);
if(x >= 0.0)
{
y = x - y * floor(x / y);
}
else
{
y = x - y * ceil(x / y);
}
return y;
}
fmod函数的扩展代码如下
/****************************************************************
求 x / y 的余数
nFlag [in] 1 返回的余数大于等于零
-1 返回的余数小于等于零
2 返回绝对值最小的余数,若同时出现 ±(y / 2) 则取正值
-2 返回绝对值最小的余数,若同时出现 ±(y / 2) 则取负值
0 与 2 相同
****************************************************************/
double fmodEx(double x,double y,int nFlag)
{
y = fabs(y);
if(nFlag < -2)
{
nFlag = -2;
}
else if(nFlag > 2)
{
nFlag = 2;
}
switch(nFlag)
{
case 1://返回的余数大于等于 0
y = x - y * floor(x / y);
break;
case -1://返回的余数小于等于 0
y = x - y * ceil(x / y);
break;
case -2://返回的绝对值最小的余数,优先考虑 - y / 2
y = x - y * floor(x / y + 0.5);
break;
default://返回的绝对值最小的余数,优先考虑 + y / 2
y = x - y * ceil(x / y - 0.5);
break;
}
return y;
}
3、格点
现在要在区间之间插入若干点,其中n是一个整数,d是一段固定的距离(只考虑正值),如:100米、1000米……
计算的时候要注意,不要写 for(double n = ceil(a/d);n <= floor(b/d);n++) 这样的代码,因为double是有误差的,这样会导致误差积累。应该这样:
double n0 = ceil(a/d);
double x0 = n0 * d;
int nMax = (int)(floor(b/d) - n0 + 0.1);
double x;
for(int i = 0;i <= nMax;i++)
{
x = x0 + i * d;
}
下面的函数专门用来获取格点
/**********************************************************
获取区间[a,b]之间的格点,格点坐标是 d 的整数倍
x0 [out] 返回起始格点。
若 d 大于零,x0 是坐标最小的格点
若 d 小于零,x0 是坐标最大的格点
返回:获得格点的个数 nCount
格点坐标为 x0 + n * d。n = 0,1,2,...nCount - 1
**********************************************************/
long GetLatticePoints(double a,double b,double d,double*x0)
{
long nCount = 0; //格点个数
double nMin; //n 的最小值
double nMax; //n 的最大值
a /= d;
b /= d;
if(a > b)
{
double t;
t = a;
a = b;
b = t;
}
nMin = ceil(a);
nMax = floor(b);
if(nMax >= nMin)
{
nCount = (long)(nMax - nMin + 1.1);
if(x0)
{
*x0 = nMin * d;
}
}
return nCount;
}