浮点值,运算符和函数有多不可靠?

pot*_*bie 7 c floating-point

我不想在一个不精确的值是一个令人讨厌的时候引入浮点数,所以我有几个问题关于你何时可以安全地使用它们.

只要不溢出有效数字的数量,它们对整数是否精确?这两个测试总是正确的:

double d = 2.0;
if (d + 3.0 == 5.0) ...
if (d * 3.0 == 6.0) ...
Run Code Online (Sandbox Code Playgroud)

您可以依赖什么数学函数?这些测试总是如此:

#include <math.h>

double d = 100.0;
if (log10(d) == 2.0) ...
if (pow(d, 2.0) == 10000.0) ...
if (sqrt(d) == 10.0) ...
Run Code Online (Sandbox Code Playgroud)

这个怎么样:

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 */
Run Code Online (Sandbox Code Playgroud)

我想你可以总结这个问题:1)浮点类型是否可以保存所有整数的精确值,直到float.h中有效数字的数字?2)所有浮点运算符和函数是否保证结果最接近实际数学结果?

tmy*_*ebu 6

我也发现不正确的结果令人反感.

在通用的硬件,你可以依靠+,-,*,/,和sqrt工作,并提供正确的舍入结果.也就是说,它们提供最接近其参数或参数的和,差,乘积,商或平方根的浮点数.

一些库函数,特别是log2和log10和exp2和exp10,通常有可怕的实现,甚至没有忠实地圆润.忠实圆形意味着函数提供两个浮点数中的一个包围确切结果.大多数现代pow实现都有类似的问题 很多这些功能甚至可以打击log10(10000)和pow(7, 2).因此,即使在确切的情况下,涉及这些功能的相等比较也会产生麻烦.

sin,cos,tan,atan,exp,并log有我最近遇到的每一个平台上忠实全面的实现.在坏日子,对使用的x87 FPU来评估处理器sin,cos和tan,你会得到可怕的错误输出,用于稍大输入你会得到输入回更大的投入.CRlibm具有正确的舍入实现; 这些都不是主流,因为据我所知,他们的情况比传统的忠实实现更糟糕.

比如像copysign和nextafter和isfinite正确的所有工作. ceil并floor和rint朋友始终提供准确的结果. fmod和朋友一样. frexp和朋友一起工作. fmin和fmax工作.

有人认为通过计算四舍五入到a 来fma(x,y,z)计算计算是个好主意,然后将结果添加并舍入为a .您可以在现代平台上找到此行为.这是愚蠢的,我讨厌它.x*y+zx*ydoublezdouble

我对C库中的双曲线trig,gamma或Bessel函数没有经验.

我还应该提一下,针对32位x86的流行编译器通过一组不同的,破坏的规则来播放.由于x87是唯一受支持的浮点指令集,并且所有x87算法都使用扩展指数完成,因此导致双精度下溢或溢出的计算可能无法下溢或溢出.此外,由于x87默认情况下也使用扩展的有效数字,因此您可能无法获得所需的结果.更糟糕的是,编译器有时会将中间结果溢出到精度较低的变量中,因此您甚至无法依赖于计算,double而是以扩展精度完成.(Java有一个使用80位寄存器进行64位数学运算的技巧,但它非常昂贵.)

long double如果您的目标是32位x86,我建议坚持使用算术.编译器应该设置FLT_EVAL_METHOD为适当的值,但我不知道这是否是普遍的.