有没有C/C++的浮动问题?

首先,我没有用英语学习数学,所以我可能在课文中使用错误的单词。

浮点数可以是有限的 (42.36) 和无限的 (42….)

在 C/C++ 中,数字以 2 为基数存储。我们的大脑以 10 为基数存储浮点数。

问题是——

许多(很多, )以 10 为基数的浮点数,即 ,在 2 基数上没有精确值,反之亦然。

这在大多数情况下没有意义。 最后一位数字可能会偏移 1 位数字 – 这不是问题。

当我们计算两个实际上是整数的浮点数时,问题就出现了。 C 上的 99.0/3.0 产生 33.0 以及 32.9999…99。 如果你将它转换为整数,你会大吃一惊。 因此,我总是在 C 中舍入之前添加一个特殊值(对于给定类型和架构为 2*min)。我应该使用它吗?

我运行了一些测试,似乎浮点除法总是按预期工作。 但有些测试还不够,因为问题是依赖于架构的。 有没有人确定它是否被处理,以及在什么级别上处理 – 在浮点类型本身中还是仅在舍入和缩短函数中?

PS 如果有人能澄清我刚刚开始的内容,那就太好了。

更新

人们在官方文件中指出,浮点运算是非确定性的。 剩下的问题是 – 像 ceil 这样的数学函数会处理它们还是我应该自己处理? 每次我们谈论这些功能时,初学者都必须指出这一点,否则他们就会偶然发现这个问题。

C 和 C 表示浮点和的格式是标准化的(IEEE 754),并且您描述的问题是该表示所固有的。 由于它是用 C 实现的,因此它的浮点类型容易出现同样的舍入问题。

浮点总和是一个更高级别的抽象,但由于大多数(全部?)现代 CPU 使用浮点计算,因此您也可能会遇到这种舍入错误。

换句话说:只有选择不将浮点类型基于底层架构的语言/库才能在某种程度上规避舍入问题,但由于底层硬件不直接支持其他表示形式,因此必然会造成性能损失。 因此,可能大多数语言都会坚持这个标准,特别是因为它的局限性是众所周知的。

实数本身,包括浮点数,在任何数学意义上都不是“无限的”。 它们可能有无限的十进制表示形式,但这只是我们编写它们(或将它们存储在计算机中)方式的技术问题。 但事实上,还指定了 ∞ 和 -∞ 值,它们是实际的无穷大……但它们并不代表实数,而且在很多方面在数学上都相当可怕。

另外……“如果你将其转换为整数,那么”你永远不应该“”将浮点数转换为整数,这是不可能的:你只能将它们四舍五入为整数。如果你这样做,例如四舍五入,它真的很安全,当然

> 圆 $99/3

33

而ghci则用浮点计算除法。

唯一始终不安全的事情是:

除了这里的其他很好的答案之外,粗略地说,无论您使用哪种语言交互,您都会遇到完全相同的问题,而且我想指出,许多语言都有用于其他类型数字的库。 一些标准方法是使用定点算术(其中许多细微差别来自浮点)或有理数。 还提供了可以计算实数和次圆数的库。

此外,由于其类型类机制,使用这些替代类型的数字特别方便,这意味着使用这些其他类型的数字进行算术看起来和感觉起来与通常使用 Float 和 进行算术完全相同; 但你会得到替代类型的更好(和更差!)的属性。 例如,通过正确的导入,您可以看到:

10

11

12

13

14

15

16

>99/3::

33.0

>99/3 :: 固定 E12

33.0

>99/3::

33%1

> 99/3 :: 真实

33.0

>99/3::

33

> 98/3 ::

98%3

> sqrt 2::CReal

1.

> (-5)::

e(20) + e(20)^9 – e(20)^13 – e(20)^17

数字在内部表示为 C 双精度数,因此您将遇到浮点算术固有的所有问题。 但它还包括“修复”明显情况的算法。 您给出的示例 32.99999… 被识别为 33.0。 从 2.7 和 3.1 开始,他们使用 Gay 算法来做到这一点; 即四舍五入到原始值的最短字符串。 您可以在 3.1 发行说明中找到说明。 在早期版本中,它仅四舍五入到小数点后 17 位。

正如他们自己警告的那样,这并不意味着它将作为十进制数工作。

>>> 1.1 + 2.2

3.30003

>>> 1.1 + 2.2 == 3.3

错误的

(但这应该已经敲响了你的警钟,因为比较浮点数是否相等从来都不是一件好事)

如果您想确保精确到小数位(例如,如果您正在处理财务),您可以使用标准库中的模块。 如果要表示小数,可以使用分数,但它们都比普通数字慢。

10

11

12

13

14

>>>

>>> .(1.1) + .(2.2)

('3.35910')

# 是完整的点,不是我输入的内容!

>>> .('1.1') + .('2.2')

('3.3')

# 现在好了。

>>> .('1.1') + .('2.2') == 3.3

错误的

>>> .('1.1') + .('2.2') == .(3.3)

错误的

>>> .('1.1') + .('2.2') == .('3.3')

真的

是的,这是一个问题。

Float 不要求是 IEEE 单精度和双精度浮点,但强烈建议使用。 GHC 遵循该建议。 IEEE 浮点数在所有语言中都存在相同的问题。 其中一些由 LIA 标准处理,但仅在“库”中实现。 (不,我不确定它是什么库或者它是否存在。)

这个很棒的答案显示了各种其他数字表示形式,它们要么是(例如)的一部分,要么可以从(Fixed、CReal 和)获得。

,已修复并且可能有类似的库; 固定类型有点类似于 .Net 类型。 CReal也是可能的,但我认为它可能会利用按需调用的优势,并且可能很难直接移植到; 它也很慢。

好了,今天的主题就讲到这里吧,不管如何,能帮到你我就很开心了,如果您觉得这篇文章写得不错,欢迎点赞和分享给身边的朋友。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注