正如(希望)你们大多数人都知道,浮点运算不同于实数运算。对于初学者来说,这是不精确的。许多数字,尤其是小数 (0.1, 0.3) 无法表示,导致这样的问题。可以在此处找到更详尽的列表。
有没有内置支持更接近实数算术的通用语言?如果没有,有什么好的库支持这个?
编辑:任意精度
decimal
数据类型不是我想要的。我希望能够表示数字,如1/3
,sqrt(3)
或1 + 2i
。
虽然我不想这么说,Fortran。它广泛支持任意精度算术和大量支持大数计算。它既古老又粗俗,但它完成了工作。
您的示例中使用的所有数字都是代数数,并且可以有限地表示为具有整数系数的多项式的根。
对于一般的实数则不能这样说,当人们认为实数不可数时很容易看出这一点,但计算机程序的集合是可数的。因此,大多数实数在代码中不会有有限表示。
您正在寻找的是符号计算(MATLAB 和其他用于数学和工程的工具都擅长它)。
如果您想要一种通用语言,我认为 C# 中的表达式树是一个很好的起点。本质上,存储表达式的能力(而不是将表达式计算为实数值)是能够执行符号计算的关键。注意表达式树不提供符号计算,它只提供支持符号计算的数据结构。
这个问题很有趣,但也引发了一些问题。首先,出于基数的原因,您将永远无法使用(甚至理论上无限的)计算机来表示所有实数。
您正在寻找的是“符号数字”数据类型。您可以想象某种表达式树,具有预定义的常量、算术运算,也许还有代数(多项式的根)和超越(exp、sin、cos、log 等)函数。
现在故事的有趣部分:你找不到一个算法来判断两个这样的树是否代表相同的数字(或等效地,它测试这样的树是否为零)。我不会说任何精确的东西,但作为一个提示,这类似于停止问题(对于计算机科学家)或哥德尔不完全定理(对于数学家)。
这使得这样的类非常无用。
对于实数的某些子域,您有规范形式,例如有理数的 a/b,或有理数的有限代数扩展(复数有理数的 a/b + ic/d,a/b + sqrt(2) * a/ b 代表 Q[sqrt(2)] 等)。这些可用于表示某些特定的代数数集。
实际上,这是您需要的最复杂的事情。如果您有特殊需要,例如浮点数范围(为了证明某些结果在指定区间内,这可能是您可以获得的最接近实数)或任意精度数,您可以在任何地方免费使用类。谷歌boost::range
前者,gmp
后者。
有几种语言支持有理数和复数。例如, Scheme支持任意精确的有理数,以及具有有理数、浮点数或整数系数的复数:
> (+ 1/2 1/3)
5/6
> (* 3 1+1/2i)
3+3/2i
> (+ 1/2 .5)
1.0
如果您想超越有理数或具有有理系数的复数,进入诸如e之类的代数数sqrt(2)
或封闭形式的数字,您可能不得不超越通用编程语言,并使用特殊用途的数学语言,如 Mathematica 或千里马。
爪哇:java.math.BigDecimal
C#:decimal
很多语言都支持这一点:Java 有BigDecimal
,Perl 有Math::BigFloat
,Math::BigRat
Haskell 有,维基百科Integer
中列出了很多库和语言。
Ada 原生支持定点数学和浮点数。只要数字的指数保持在范围内,定点就可以比浮点更精确。
如果您需要浮点,但比 IEEE 提供的精度更高,那么几乎每种语言都有 bignum 包。
我认为这是你能做的最好的事情。这两种方案都不能准确地表示重复的小数(如 1/3)。可能会提出一个可行的方案,但我知道没有一种语言支持这种内置类型的东西。即使这样对无理数(如 pi 和 e)也无济于事。我相信甚至有一个定理说总会有不可表示的数字,无论你想出什么方案。
编辑:任意精度十进制数据类型不是我要找的。我也希望能够表示 1/3、sqrt(3) 或 1 + 2i 等数字。
Ruby 有一个 Rational 类,所以 1/3 可以精确地表示为 Rational(1,3)。它还有一个 Complex 类。
Scheme定义了有理数、大数、浮点数和复数。不需要实现来支持所有这些,但如果它们存在,您可以混合它们,它们将“正确”。
虽然它不是“内置的”,但我认为 C++(也许是 C#)是你最好的选择。为此目的编写了一些课程。