我想写一个fmod((函数
double fmod(double x, double y) {
double mod = x;
while(mod >= y)
{
mod -= y;
}
return mod;
}
但fmod(1.2,0.05(返回0.05
尽管标题询问了不正确的比较,但所示程序中的比较是正确的。它是程序中唯一正确的浮点运算;所有其他的都有错误(与实数算术相比(。
在fmod(1.2, 0.05)
中,1.2和0.5都不能用C实现中使用的double
格式表示。源代码中的这些数字四舍五入到最接近的可表示值1.19999999999999995555910790149937383830547332763671875和0.05000000000000000277555756156289135105907917022705078125。
然后,在mod -= y;
中的减法中,精确的实数运算结果1.1449999999999528155214534308437019565410614013671875是不可表示的,因此将其四舍五入为1.1449999999999 11182158029987476766109466552734375。
在计算过程中,类似的错误仍在继续,直到最终产生0.0499999999999994615418330567990778945386409759521484375。在每个点,比较mod >= y
正确地评估mod
是否大于或等于y
。当mod
小于y
时,循环停止。
但是,由于中间的错误,产生的结果0.04999999999999946154183300567990778945386409759521484375不等于1.1999999999995555910790149937383830547332763671875除以0.05000000000000000277555756156289135105907917022705078125的余数。可以使用标准fmod
函数计算正确的结果,该函数返回0.04999999999999989175325509904723730869591236114501953125。
请注意,当定义名为fmod
的函数时,C标准不会定义行为,因为这与该名称的标准库函数冲突。您应该给它一个不同的名称,例如fmodAlternate
。
在fmod
例程中,可以避免错误。可以实现fmod
,这样它就可以为给定的参数返回精确的结果。(这是可能的,因为结果总是在浮点范围的一个区域中,该区域足够精细[指数足够低],可以准确地表示实际算术结果。(但是,无法更正提供参数时的错误:在C实现使用的double
格式中,不可能表示1.2或0.05。源代码fmod(1.2, .05)
将始终计算fmod(1.1999999999999999555910790149937383830547332763671875, 0.05000000000000000277555756156289135105907917022705078125)
,即0.04999999999999989175325509904723730869591236114501953125。
另一种选择是用不同的方式表示数字。例如,您可以将这些数字按因子100缩放,fmod(120, 5)
将返回0。什么解决方案是合适的取决于你试图解决的问题的具体情况。