C语言浮点数float运算注意事项

C语言中数字计算除了整数外就是浮点数(float和double),由于浮点数表示法在二进制中是采用近似表示,所以有些浮点数表示是不精确的,下面将介绍C语言中浮点数计算的注意事项。

C语言中数字计算除了整数外就是浮点数(float和double),由于浮点数表示法在二进制中是采用近似表示,所以有些浮点数表示是不精确的,下面将介绍C语言中浮点数计算的注意事项。

1、一个 float 变量赋值为 3.1 时, 为什么 printf 输出的值为 3.0999999?

大多数电脑都是用二进制来表示浮点和整数的。 在十进制里, 0.1 是个简单、精确的小数, 但是用二进制表示起来却是个循环小数 0.0001100110011 . . . 。 所以3.1 在十进制内可以准确地表达, 而在二进制下不能。

在对一些二进制中无法精确表示的小数进行赋值或读入再输出时, 也就是从十进制转成二进制再转回十进制, 你会观察到数值的不一致. 这是由于编译器二进制/十进制转换例程的精确度引起的, 这些例程也用在 printf 中。 

2、执行一些开方根运算, 可是得到一些疯狂的数字

确定你用了 #include <math.h>, 以及正确说明了其它相关函数返回值为double。 另外一个需要注意的库函数是 atof(), 其原型说明在 <stdlib.h> 中。 

3、做一些简单的三角函数运算, 也引用了 #include <math.h>

做一些简单的三角函数运算, 也引用了 #include <math.h>,可是一直得到编译错误 “undefined: sin” (函数 sin 未定义)。确定你真的连接了数学函数库 (math library)。 例如, 在 Unix 或Linux 系统中, 有一个存在了很久的bug, 你需要把参数 -lm 加在编译或连接命令行的最后。

4、浮点计算程序表现奇怪, 在不同的机器上给出不同的结果

如果问题并不是那么简单, 那么回想一下, 电脑一般都是用一种浮点的格式来近似的模拟实数的运算, 注意是近似, 不是完全。 下溢、 误差的累积和其它非常规性是常遇到的麻烦。

不要假设浮点运算结果是精确的, 特别是别假设两个浮点值可以进行等价比较。 也不要随意的引入 “模糊因素”; 

这并不是 C 特有的问题, 其它电脑语言有一样的问题。 浮点的某些方面被通常定义为 “中央处理器 (CPU) 是这样做的” (参见问题 11.34), 否则在一个没有 “正确” 浮点模型的处理器上, 编译器要被迫做代价非凡的仿真。

5、有什么好的方法来验对浮点数在 “足够接近” 情况下的等值?

浮点数的定义决定它的绝对精确度会随着其代表的值变化, 所以比较两个浮点数的最好方法就要利用一个精确的阈值。 这个阈值和作比较的浮点数值大小有关。 不要用下面的代码:

double a, b;
...
if (a == b) /* 错! */

要用类似下列的方法:

#include <math.h>
if (fabs(a - b) <= epsilon * fabs(a))

epsilon 被赋为一个选定的值来控制 “接近度”。 你也要确定 a 不会为 0。

6、怎样取整数?

最简单、 直接的方法:

(int)(x + 0.5)

这个方法对于负数并不正常工作。 可以使用一个类似的方法:

(int)(x < 0 ? x - 0.5 : x + 0.5)

7、为什么 C 不提供乘幂的运算符?

因为提供乘幂指令的处理器非常少。 C 有一个 pow() 标准函数, 原型说明在<math.h>。 而对于小的正整数指数, 直接用乘法一般会更有效。

8、为什么我机器上的 <math.h> 没有预定义常数 MPI?

这个常数不包含在标准内, 它应该是定义准确到机器精度的 π 值。 如果你需要用到 π, 你需要自己定义, 或者用 4*atan(1.0) 或 acos(-1.0) 来计算出来。

9、怎样测试 IEEE NaN 以及其它特殊值?

许多实现高质量 IEEE 浮点的系统会提供简洁的工具去处理这些特殊值。 例如, 在 <math.h> 以非标准扩展功能, 或可能以 <ieee.h> 或 <nan.h> 提供预定义常数, 及象 isnan() 这类的函数。 这些工具的标准化进程正在进行中。 一个粗陋但通常有效的测试 NaN 的方法:

#define isnan(x) ((x) != (x))

虽然一些不支持 IEEE 的编译器可能会把这个判断优化掉。C99 提供 isnan(), fpclassify() 及其它一些类别的例程。必要时, 还可以用 sprintf() 格式化需测试的值, 在许多系统上, 它会产生“NaN” 或 “Inf” 的字符串。

10、在 C 中如何很好的实现复数?

这其实非常直接, 定义一个简单结构和相关的算术函数就可以了。 C99 在标准中支持复数类别。

11、我要寻找一些实现以下功能的程序源代码

快速傅立叶变换(FFT)、 矩阵算术 (乘法、 倒置等函数)、 复数算术。Ajay Shah 整理了一个免费算术软件列表。 这个列表在互联网有广泛的归档。 

12、Turbo C 的程序崩溃, 显示错误为 “floating point formats not linked” (浮点格式未连接)

一些在小型机器上使用的编译器, 包括 Turbo C (和 Richie 最初用在 PDP-11上的编译器), 编译时会忽略掉某些它认为不需要的浮点支持。 特别是用非浮点版的 printf() 和 scanf() 以节省一些空间, 也就是忽略处理 %e、 %f 和 %g 的编码。然而, Borland 用来确定程序是否使用了浮点运算的探测法并不充分, 程序员有时必需调用一个空浮点库函数 (例如 sqrt(), 或任何一个函数都可以) 以强制装载浮点支持。

我们一定不要当三等公民:等下班、等薪水、等退休。
2 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号