繁体   English   中英

Python和Haskell是否存在C / C ++的浮动不确定性问题?

[英]Do Python and Haskell have the float uncertanity issue of C/C++?

首先,我不是用英语学习数学,所以我可能会在文本中使用错误的单词。

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

在C / C ++中,数字存储在基数2中 我们的思想在10号基地运行浮标

问题是 -

many (a lot, actually) of float numbers with base 10, that are finite, have no exact finite representation in base 2, and vice-versa.

这在大多数时候并不意味着什么。 double最后一位可能会偏离1位 - 这不是问题。

当我们计算两个实际上是整数的浮点数时会出现问题。 C ++上的99.0/3.0可以得到33.0以及32.9999...99 如果你将它转换为整数然后 - 你会感到惊讶。 出于这个原因,我总是在C中向上舍入之前添加一个特殊值(给定类型和体系结构的2 *最小值) 我应该用Python做吗?

我已经在Python中运行了一些测试, 看起来浮动除法总是按预期结果。 但是一些测试还不够,因为问题依赖于架构。 有人确切知道它是否被照顾,以及在什么级别 - 浮动类型本身或仅在舍入和缩短功能?

PS如果有人可以为Haskell澄清同样的事情,我只是开始 - 它会很棒。

UPDATE乡亲们指出了一份正式文件,说明存在浮点运算的不确定性。 剩下的问题是 - 像ceil这样的math函数会照顾它们还是应该自己完成? 每当我们谈到这些功能时, 必须向初学者用户指出这一点,否则他们都会偶然发现这个问题。

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

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

换句话说:只有选择将其浮点类型基于底层架构的语言/库可能能够在一定程度上规避IEEE754舍入问题,但由于底层硬件不直接支持其他表示,因此必须是性能惩罚。 因此,大多数语言可能会坚持这一标准,尤其是因为它的局限性是众所周知的。

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

另外......“如果你将它转换为整数然后”你永远不应该将“浮点数”转换为整数,它实际上是不可能的:你只能将它们四舍五入为整数。 如果你用Haskell的round做到这一点,那肯定是非常安全的

序曲> $ 99/3轮
33

虽然ghci用浮点计算除法。

唯一总是不安全的事情:

  • 当然,从float到int的隐式转换是完全疯狂的,而且在C语言中肯定是错误的。 Haskell和Python都是正确的强类型 ,因此这些东西不会偶然发生。

  • 浮点通常不应该与任何特定的完全相同 无论如何,期望这样做并不是真正有用,因为对于实际的实数,任何一个都是空集 ,这大致意味着两个实数相等的唯一方法是它是否有如此深刻的数学原因。 但是对于任何分布,例如来自物理过程,平等的概率恰好为零 ,那你为什么要检查呢?
    比较数字OTOH和< ,是完全安全的(除非你处理巨大数字之间的非常小的差异,或者你通过检查>来使用它来“模拟”相等)。

是的,这是Python中的一个问题。

请参阅https://docs.python.org/2/tutorial/floatingpoint.html

Python内部将数字表示为C双精度数,因此您将拥有浮点算术所固有的所有问题。 但它还包括一些算法来“修复”明显的案例。 您给出的示例,32.99999 ...被识别为33.0。 从Python 2.7和3.1开始,他们使用Gay的算法做到这一点; 也就是说,回转到原始值的最短字符串。 您可以在Python 3.1发行说明中看到说明。 在早期版本中,它只是舍入到前17个小数位。

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

>>> 1.1 + 2.2
3.3000000000000003
>>> 1.1 + 2.2 == 3.3
False

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

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

>>> import decimal
>>> decimal.Decimal(1.1) + decimal.Decimal(2.2) 
Decimal('3.300000000000000266453525910')
# Decimal is getting the full floating point representation, no what I type!

>>> decimal.Decimal('1.1') + decimal.Decimal('2.2')
Decimal('3.3')
# Now it is fine.
>>> decimal.Decimal('1.1') + decimal.Decimal('2.2') == 3.3
False
>>> decimal.Decimal('1.1') + decimal.Decimal('2.2') == decimal.Decimal(3.3)
False
>>> decimal.Decimal('1.1') + decimal.Decimal('2.2') == decimal.Decimal('3.3')
True

除了这里的其他精彩答案之外,大致说IEEE754具有完全相同的问题,无论你使用哪种语言,我都想指出,许多语言都有其他类型的数字库。 一些标准方法是使用定点算术(很多但不是全部,IEEE754的细微差别来自浮点)或有理数。 Haskell也是可计算实数和分圆数的库。

另外,使用这些替代类型的数字在Haskell中由于其类型类机制特别方便,这意味着使用这些其他类型的数字进行算术看起来和感觉完全相同,并使用您通常的IEEE754 FloatDouble s进行算术运算; 但是你会得到备用类型的更好(和更差!)的属性。 例如,通过适当的导入,您可以看到:

> 99/3 :: Double
33.0
> 99/3 :: Fixed E12
33.000000000000
> 99/3 :: Rational
33 % 1
> 99/3 :: CReal
33.0
> 99/3 :: Cyclotomic
33
> 98/3 :: Rational
98 % 3
> sqrt 2 :: CReal
1.4142135623730950488016887242096980785697
> sqrtInteger (-5) :: Cyclotomic
e(20) + e(20)^9 - e(20)^13 - e(20)^17

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

这个伟大的回答显示了各种其他的数字表示是哈斯克尔(喜欢的任一部分合理 ),或可从hackage像( 固定CReal割圆 )。

Rational,Fixed和Cyclotomic可能有类似的Python库; 固定有点类似于.Net Decimal类型。 CReal也可能,但我认为它可能会利用Haskell的需求调用,并且很难直接移植到Python; 它也很慢。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM