math_fmod是lua库函数math.fmod的具体实现,math_fmod的声明如下:
static int math_fmod (lua_State *L);
math_fmod接受两个参数,命名这两个参数为x、y,math_fmod求x除以y的余数。
math_fmod的实现:
static int math_fmod (lua_State *L) {
if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) {
lua_Integer d = lua_tointeger(L, 2);
if ((lua_Unsigned)d + 1u <= 1u) { /* special cases: -1 or 0 */
luaL_argcheck(L, d != 0, 2, "zero");
lua_pushinteger(L, 0); /* avoid overflow with 0x80000... / -1 */
}
else
lua_pushinteger(L, lua_tointeger(L, 1) % d);
}
else
lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1),
luaL_checknumber(L, 2)));
return 1;
}
math_fmod首先判断传入参数的类型。如果两个参数都是整数类型,则单独处理求余;否则调用C语言的库函数fmod对浮点数进行余数求解。 当参数x、y都是整数时,lua对第二个参数即y做了安全判断,避免算术运算的异常。luaL_argcheck(L, d != 0, 2, "zero");
对y等于0做了检查;当y等于-1时,如果x等于0x8000…(LUA_MININTEGER),而0x8000…除以-1的结果是无法用当前的整数表示的(因为这个值实际等于LUA_MAXINTEGER+1);为避免溢出,lua_pushinteger(L, 0);
对此情况做了一个简单处理:直接将余数设置为0。 当参数x、y不都是整数时,lua先调用luaL_checknumber尝试将其转换成浮点数;转换成功后,调用C标准库方法fmod求x除以y的余数。注意,求得的余数结果可能并不是你想要的,因为浮点数是存在误差的。看一个例子:
// test.c
#include <stdio.h>
#include <math.h>
int main ()
{
printf( "%lf\n%lf\n", 0.6 / 0.2, fmod(0.6,0.2));
return 0;
}
这里顺便提醒一下:如果你是用gcc编译上述代码,记得在编译时加上参数**-lm**,表示链接数学库,否则gcc可能会报告fmod是未定义的引用。完整的编译并执行程序命令可参考:
gcc -lm -o test test.c && ./test
在我的PC上,fmod(0.6,0.2)的打印结果是0.2,这个结果明显是错误的,因为0.6除以0.2能够整数,其商为3;那为什么**fmod(0.6,0.2)**的结果是0.2呢?根本原因就是浮点数是有精度限制的,0.6除以0.2理应等于3,但是在计算机中的结果可能并不是这样;对test.c代码进行修改:
//test1.c
#include <stdio.h>
#include <math.h>
int main ()
{
printf( "%0.17lf\n%0.17f\n", 0.6 / 0.2, fmod(0.6,0.2));
return 0;
}
运行此程序,得到打印结果: 2.99999999999999956 0.19999999999999996 0.6除以0.2实际等于2.99999999999999956(近似于3),这就导致了0.6除以0.2的余数是0.19999999999999996(近似于0.2)。所以求两个浮点数的余数时,要特别小心,实际的结果和你预期的结果可能是南辕北辙的。