我从来不明白为什么浮点数据类型被认为是近似值,而十进制数据类型被认为是精确的。我正在寻找一个好的解释,谢谢。
4 回答
嗯,这取决于你的观点。float
和decimal
(假设您的意思是 C# 中的类型)都表示精确值。然而,在这两种情况下,转换(和算术)都可能以近似值结束,其中存储的值只是“与理论精确值最接近的可用值”。
特别是 - 重要的是“float
近似”想法的正常原因 - 是代码中文字值的转换:
float f = 0.1f;
decimal d = 0.1m;
这些文字以十进制数表示 - 0.1 不能精确地表示为二进制浮点数。但是,它可以精确地表示为十进制浮点数。
如果我们使用二进制作为文字,例如
// Imaginary syntax - doesn't work in C#!
float f = 0b0.11f;
那么这里就不会有任何近似值 - 该值将与十进制值 0.75 完全相同。
基本上,这完全是关于人类对以 10 为底的数字的偏见。从一个单手有 3 个手指的外星人的角度来看,自然地代表以 3 为底的值,两者float
都是decimal
“近似值”。
(在 C# 中,有效数字与值的范围之间还有其他显着差异float
,但这是另一回事。)decimal
好吧,你是对的 - 做出如此笼统的陈述是一种误导。要完全理解,您需要掌握两件事。
首先,十进制用于存储(精确)具有固定小数位数的十进制值。通常是钱(例如,小数点是美分)。这是一个非常具体的用例。它不是任何价值的确切商店;它仅适用于具有固定小数点的十进制值,并且实现是为了正确地做到这一点而定制的。
其次,浮点数旨在成为一种更通用的数据类型 - 它们用于存储“任何”值 - 并且实现反映了这一点(例如,实现旨在覆盖广泛的规模并尽可能有效地支持操作)。特别是,它使用不能精确表示所有十进制值的二进制表示。因此,例如,它可以精确存储 0.5,但不能精确存储 0.1。这只是使用的二进制 - 基数 2 - 表示的一个事实,但这意味着对于金钱来说,浮点数不是一个好主意:如果你不能将 0.10 完全存储为浮点数,那么任何涉及 10 美分的计算都可能会累积意外错误。
换句话说,两者都有其局限性。十进制比浮点“更精确”的唯一方法是它更容易理解:它确实工作的值是明确定义的、有用的,并且与我们使用的“自然”以 10 为基数的表示相匹配。相比之下,很难理解哪些值将由浮点数精确存储,哪些不是,因为它们依赖于底层的 base 2 表示。
一个decimal
值完全等于其字符串表示的值(编辑:十进制,如 Skeet 注释),并且decimal
还能够精确地表示具有有限位数的十进制数字。另一方面,一个float
值通常会与其字符串表示不同,并且有许多十进制数(例如 0.1)不能准确表示为float
.
您可以在此处获得所有血腥细节,但基本上 IEEE 浮点将数字存储为整数尾数乘以 2 的整数幂。就像某些分数(如三分之一)不能精确地表示为不重复的以 10 为底的十进制数一样,许多分数也不能精确地表示为二进制分数。这通常令人惊讶,因为虽然习惯于 1/3 作为重复小数,但我们直观地认为 1/5 很容易表示为 0.2,但在二进制中它是重复小数(二进制?),不能用有限数精确表示位。