当一个不精确的值会造成灾难时,我不想引入浮点数,因此我有几个问题,关于何时可以真正安全地使用它们。
只要您不使有效数字溢出,它们是否对整数准确?这两个测试是否总是正确的:
double d = 2.0;
if (d + 3.0 == 5.0) ...
if (d * 3.0 == 6.0) ...
您可以依靠哪些数学函数?这些测试是否总是正确的:
#include <math.h>
double d = 100.0;
if (log10(d) == 2.0) ...
if (pow(d, 2.0) == 10000.0) ...
if (sqrt(d) == 10.0) ...
这个怎么样:
int v = ...;
if (log2((double) v) > 16.0) ... /* gonna need more than 16 bits to store v */
if (log((double) v) / log(2.0) > 16.0) ... /* C89 */
我猜您可以将这个问题总结为:1)浮点类型是否可以容纳所有整数的精确值,直到float.h中它们的有效位数为止?2)是否所有浮点运算符和函数都保证结果与实际数学结果最接近?
我也发现错误的结果令人反感。
在通用的硬件,你可以依靠+
,-
,*
,/
,和sqrt
工作,并提供正确的舍入结果。也就是说,它们提供最接近其一个或多个自变量的和,差,乘积,商或平方根的浮点数。
一些库函数,特别是log2
andlog10
和exp2
andexp10
传统上具有可怕的实现,甚至没有如实地实现。忠实舍入意味着函数提供两个浮点数之一,将精确结果括起来。大多数现代pow
实现都有类似的问题。这些功能很多甚至会打击确切的情况,例如log10(10000)
和pow(7, 2)
。因此,即使在确切的情况下,涉及这些功能的相等性比较也会带来麻烦。
sin
,cos
,tan
,atan
,exp
,并log
有忠实全面的实现每个平台上,我最近遇到。在过去的糟糕日子里,在使用x87 FPU评估sin
,cos
和的处理器上,tan
对于笨拙的输入,您将得到非常错误的输出,而对于较大的输入,则将得到输入。CRlibm具有正确的实现;有人告诉我,这些不是主流,因为与传统的全面实现相比,它们有更糟糕的最坏情况。
之类的东西copysign
,并nextafter
和isfinite
所有工作正常。ceil
andfloor
和rint
and朋友总是能提供准确的结果。fmod
和朋友也一样。frexp
和朋友一起工作。fmin
和fmax
工作。
有人认为通过将计算取整为a ,然后将结果加和取整为a来做fma(x,y,z)
计算是一个绝妙的主意。您可以在现代平台上找到此行为。这很愚蠢,我讨厌。x*y+z
x*y
double
z
double
我没有使用C库中的双曲三角函数,gamma或Bessel函数的经验。
我还应该提到,针对32位x86的流行编译器会使用一组不同的破碎规则。由于x87是唯一受支持的浮点指令集,并且所有x87算术均使用扩展的指数进行,因此以双精度引起下溢或上溢的计算可能无法下溢或上溢。此外,由于x87在默认情况下也使用扩展的有效数字,因此您可能无法获得所需的结果。更糟糕的是,编译器有时会将中间结果溢出到精度较低的变量中,因此您甚至不能依靠double
s以扩展精度进行计算。(Java有一个技巧,可以使用80位寄存器来执行64位数学运算,但是这很昂贵。)
long double
如果您的目标是32位x86,我建议对s坚持算术。编译器应该设置FLT_EVAL_METHOD
为适当的值,但是我不知道这是否是通用的。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句