.NET 中十进制、浮点和双精度的区别?

Posted

技术标签:

【中文标题】.NET 中十进制、浮点和双精度的区别?【英文标题】:Difference between decimal, float and double in .NET? 【发布时间】:2010-10-11 17:42:16 【问题描述】:

.NET 中decimalfloatdouble 有什么区别?

什么时候有人会使用其中之一?

【问题讨论】:

趣味文章zetcode.com/lang/csharp/datatypes 相关:sandbox.mc.edu/~bennet/cs110/flt/dtof.html 您不能使用十进制与本机代码互操作,因为它是 .net 特定的实现,而浮点数和双精度数可以由 CPU 直接处理。 【参考方案1】:

floatdouble 是 floating binary point types。换句话说,它们代表这样一个数字:

10001.10010110011

二进制数和二进制点的位置都在值内编码。

decimal 是floating decimal point type。换句话说,它们代表这样一个数字:

12345.65789

同样,小数点的数字和位置都被编码在值中——这就是decimal仍然是浮点类型而不是定点类型的原因。

需要注意的重要一点是,人类习惯于以十进制形式表示非整数,并期望以十进制表示的精确结果;并非所有十进制数都可以用二进制浮点数精确表示——例如 0.1——所以如果你使用二进制浮点值,你实际上会得到一个 0.1 的近似值。使用浮点小数点时,您仍然会得到近似值 - 例如,无法精确表示 1 除以 3 的结果。

至于什么时候用:

对于“自然精确小数”的值,最好使用decimal。这通常适用于人类发明的任何概念:财务价值是最明显的例子,但也有其他例子。例如,考虑给潜水员或溜冰者的分数。

对于那些无法真正准确测量的自然产物的值,float/double 更合适。例如,科学数据通常会以这种形式表示。在这里,原始值一开始就不是“十进制精度”,因此保持“十进制精度”对于预期结果并不重要。浮点二进制点类型的处理速度比小数要快得多。

【讨论】:

float/double 通常不将数字表示为101.101110,通常表示为1101010 * 2^(01010010) - 一个指数 @Hazzard:这就是答案的“和二进制点的位置”部分的意思。 我很惊讶它还没有被说出来,float 是一个 C# 别名关键字,而不是一个 .Net 类型。 System.Single.. singledouble 是浮点型。 @BKSpureon:好吧,只是你可以说 everything 是二进制类型,此时它变成了一个相当无用的定义。 Decimal 是一种十进制类型,它是一个表示为整数有效数和小数位数的数字,因此结果是有效数 * 10^scale,而 float 和 double 是有效数 * 2^scale。你取一个用十进制写的数字,并将小数点向右移动足够远,这样你就有了一个整数来计算有效数和小数位数。对于浮点数/双精度数,您将从一个用二进制编写的数字开始。 另一个区别:浮点32位;双 64 位;和十进制 128 位。【参考方案2】:

精度是主要区别。

Float - 7 位(32 位)

Double-15-16 位(64 位)

Decimal -28-29 有效数字(128 位)

小数的精度要高得多,通常用于需要高精度的金融应用程序中。小数比双精度/浮点数慢得多(在某些测试中高达 20 倍)。

小数和浮点数/双精度数不能在没有强制转换的情况下进行比较,而浮点数和双精度数可以。小数也允许编码或尾随零。

float flt = 1F/3;
double dbl = 1D/3;
decimal dcm = 1M/3;
Console.WriteLine("float: 0 double: 1 decimal: 2", flt, dbl, dcm);

结果:

float: 0.3333333  
double: 0.333333333333333  
decimal: 0.3333333333333333333333333333

【讨论】:

@Thecrocodilehunter:抱歉,没有。十进制可以表示所有可以用十进制表示的数字,但不能表示例如 1/3。 1.0m / 3.0m 将评估为 0.33333333...,最后有大量但有限的 3。将它乘以 3 不会返回精确的 1.0。 @Thecrocodilehunter:我认为你混淆了准确性和精确性。在这种情况下,它们是不同的东西。精度是可用于表示数字的位数。精度越高,舍入的次数就越少。没有数据类型具有无限精度。 @Thecrocodilehunter:您假设正在测量的值是完全 0.1——这在现实世界中很少见! 任何有限存储格式会将无限数量的可能值合并为有限数量的位模式。例如,float 将合并0.10.1 + 1e-8,而decimal 将合并0.10.1 + 1e-29。当然,在给定范围内,某些值可以用任何格式表示,精度为零(例如,float 可以存储高达 1.6e7 的任何整数,精度为零)——但那是仍然不是无限准确度。 @Thecrocodilehunter:你错过了我的意思。 0.1 不是特殊值!使0.10.10000001“更好”的唯一原因是人类喜欢以10为底。即使使用float值,如果您使用0.1初始化两个值相同方式,它们都将是相同的值。只是该值不会完全0.1——它将是最接近0.1 的值,可以精确地表示为float。当然,对于二进制浮点数,(1.0 / 10) * 10 != 1.0,但对于十进制浮点数,(1.0 / 3) * 3 != 1.0 也可以。 两者都没有完美精确的。 @Thecrocodilehunter:你还是不明白。我不知道该怎么说清楚了:在 C 语言中,如果你这样做 double a = 0.1; double b = 0.1; 然后 a == b 将是真的。只是ab两者 不完全等于0.1。在 C# 中,如果您执行 decimal a = 1.0m / 3.0m; decimal b = 1.0m / 3.0m;,那么 a == b 也将成立。但在这种情况下,ab都不完全等于 1/3——它们都等于 0.3333...。在两种情况中,由于表示,会损失一些准确性。你固执地说decimal 具有“无限”精度,这是false【参考方案3】:
+---------+----------------+---------+----------+---------------------------------------------------------+
| C#      | .Net Framework | Signed? | Bytes    | Possible Values                                         |
| Type    | (System) type  |         | Occupied |                                                         |
+---------+----------------+---------+----------+---------------------------------------------------------+
| sbyte   | System.Sbyte   | Yes     | 1        | -128 to 127                                             |
| short   | System.Int16   | Yes     | 2        | -32,768 to 32,767                                       |
| int     | System.Int32   | Yes     | 4        | -2,147,483,648 to 2,147,483,647                         |
| long    | System.Int64   | Yes     | 8        | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
| byte    | System.Byte    | No      | 1        | 0 to 255                                                |
| ushort  | System.Uint16  | No      | 2        | 0 to 65,535                                             |
| uint    | System.UInt32  | No      | 4        | 0 to 4,294,967,295                                      |
| ulong   | System.Uint64  | No      | 8        | 0 to 18,446,744,073,709,551,615                         |
| float   | System.Single  | Yes     | 4        | Approximately ±1.5e-45 to ±3.4e38                       |
|         |                |         |          |  with ~6-9 significant figures                          |
| double  | System.Double  | Yes     | 8        | Approximately ±5.0e-324 to ±1.7e308                     |
|         |                |         |          |  with ~15-17 significant figures                        |
| decimal | System.Decimal | Yes     | 16       | Approximately ±1.0e-28 to ±7.9e28                       |
|         |                |         |          |  with 28-29 significant figures                         |
| char    | System.Char    | N/A     | 2        | Any Unicode character (16 bit)                          |
| bool    | System.Boolean | N/A     | 1 / 2    | true or false                                           |
+---------+----------------+---------+----------+---------------------------------------------------------+

See here for more information.

【讨论】:

你忽略了最大的区别,它是用于十进制类型的基数(十进制存储为以 10 为基数,列出的所有其他数字类型都是以 2 为基数)。 Single 和 Double 的值范围在上图或源论坛帖子中未正确描述。由于我们不能轻易地为此处的文本添加上标,因此使用插入符号:Single 应该是 10^-45 和 10^38,Double 应该是 10^-324 和 10^308。此外,MSDN 的浮点数范围为 -3.4x10^38 到 +3.4x10^38。如果链接更改,请在 MSDN 中搜索 System.Single 和 System.Double。单人:msdn.microsoft.com/en-us/library/b1e65aza.aspx双人:msdn.microsoft.com/en-us/library/678hzkk9.aspx 十进制是 128 位 ... 表示它占用 16 个字节而不是 12【参考方案4】:

Decimal 结构严格适用于需要精确度的财务计算,而这种计算方式相对不允许四舍五入。然而,小数对于科学应用来说是不够的,原因如下:

由于被测量的物理问题或工件的实际限制,在许多科学计算中,一定程度的精度损失是可以接受的。在金融领域,精度损失是不可接受的。 对于大多数操作来说,十进制比浮点和双精度要慢得多(很多),主要是因为浮点运算是以二进制完成的,而十进制的东西是以 10 为底完成的(即浮点和双精度由 FPU 硬件处理,例如MMX/SSE,而小数是在软件中计算的)。 Decimal 的值范围比 double 小得令人无法接受,尽管它支持更多位数的精度。因此,Decimal 不能用于表示许多科学值。

【讨论】:

如果您在进行财务计算,您绝对必须滚动您自己的数据类型或找到一个符合您确切需求的好的库。金融环境中的准确性由(人类)标准机构定义,并且他们对如何进行计算有非常具体的本地化(时间和地理)规则。 .Net 的简单数字数据类型中没有捕获正确舍入之类的内容。进行计算的能力只是难题的一小部分。【参考方案5】:

我不会重复其他答案和 cmets 已经回答的大量好(和一些坏)信息,但我会用提示回答您的后续问题:

什么时候有人会使用其中之一?

计数值使用小数

测量值值使用浮点数/双精度值

一些例子:

钱(我们是数钱还是量钱?)

距离(我们计算距离还是测量距离?*)

分数(我们计算分数还是衡量分数?)

我们总是数钱,绝不应该衡量它。我们通常测量距离。我们经常计算分数。

* 在某些情况下,我称之为标称距离,我们可能确实想要“计算”距离。例如,也许我们正在处理显示到城市距离的国家标志,而我们知道这些距离永远不会超过一位十进制数字 (xxx.x km)。

【讨论】:

我真的很喜欢这个答案,尤其是“我们计算或衡量金钱吗?”这个问题。然而,除了钱,我想不出任何“计数”的东西,而不仅仅是整数。我见过一些使用小数的应用程序,仅仅是因为 double 的有效数字太少。换句话说,可能会使用小数,因为 C# 没有 quadruple 类型 en.wikipedia.org/wiki/Quadruple-precision_floating-point_format【参考方案6】:

float 7 位精度

double 的精度约为 15 位

decimal 的精度约为 28 位

如果您需要更高的精度,请使用 double 而不是 float。 在现代 CPU 中,两种数据类型的性能几乎相同。使用浮动的唯一好处是它们占用的空间更少。只有当你有很多时才真正重要。

我发现这很有趣。 What Every Computer Scientist Should Know About Floating-Point Arithmetic

【讨论】:

@RogerLipscombe:在没有大于 32 位的整数类型可用且使用 double 的情况下(基本上仅在那些情况下),我认为 double 适合会计应用程序就好像它是一个 53 位的整数类型(例如,容纳整数个便士,或整数个百分之一美分)。现在这类东西用处不大,但许多语言早在获得 64 位(或在某些情况下甚至是 32 位!)整数数学之前就获得了使用双精度浮点值的能力。 您的回答暗示精度是这些数据类型之间的唯一区别。鉴于二进制浮点运算通常在硬件 FPU 中实现,性能是一个显着差异。这对某些应用程序可能无关紧要,但对其他应用程序却至关重要。 @supercat double 从不适用于会计应用程序。因为 Double 只能近似十进制值(即使在自己的精度范围内)。这是因为 double 以 base-2(二进制)为中心的格式存储值。 @BrainSlugs83:使用浮点类型来保存 非整数 数量是不合适的,但在历史上,使用浮点类型的语言非常普遍可以精确地表示比它们的整数类型可以表示的更大的整数值。也许最极端的例子是 Turbo-87,其唯一的整数类型被限制在 -32768 到 +32767 之间,但其 Real 可以 IIRC 以单位精度​​表示高达 1.8E+19 的​​值。我认为对于会计应用程序来说,使用Real 来表示整数比... ...让它尝试使用一组 16 位值执行多精度数学运算。对于大多数其他语言来说,差异并没有那么大,但是很长一段时间以来,没有任何超过 4E9 的整数类型但具有单位精度高达 9E15 的 double 类型的语言非常普遍。如果需要存储大于可用的最大整数类型的整数,使用double 往往比试图捏造多精度数学更简单、更有效,尤其是考虑到处理器有执行 16x16-> 的指令时32 或...【参考方案7】:

没有人提到过

在默认设置中,浮点数 (System.Single) 和双精度数 (System.Double) 永远不会使用 Decimal (System.Decimal) 将始终使用溢出检查 溢出检查。

我是说

decimal myNumber = decimal.MaxValue;
myNumber += 1;

抛出 OverflowException

但这些不是:

float myNumber = float.MaxValue;
myNumber += 1;

&

double myNumber = double.MaxValue;
myNumber += 1;

【讨论】:

float.MaxValue+1 == float.MaxValue,就像decimal.MaxValue+0.1D == decimal.MaxValue。也许你的意思是float.MaxValue*2 @supercar 但是decimal.MaxValue + 1 == decimal.MaxValue 是不正确的 @supercar decimal.MaxValue + 0.1m == decimal.MaxValue ok System.Decimal 在无法区分整个单元之前抛出异常,但如果应用程序应该处理例如美元和美分,那可能为时已晚。【参考方案8】:

如前所述,整数是整数。他们无法存储点,例如 .7、.42 和 .007。如果您需要存储不是整数的数字,则需要不同类型的变量。您可以使用双精度型或浮点型。您以完全相同的方式设置这些类型的变量:输入doublefloat,而不是使用单词int。像这样:

float myFloat;
double myDouble;

float 是“浮点数”的缩写,仅表示末尾带有点的数字。)

两者之间的区别在于它们可以容纳的数字的大小。对于float,您的号码中最多可以有 7 位数字。对于doubles,您最多可以有 16 位数字。更准确地说,这是官方尺寸:

float:  1.5 × 10^-45  to 3.4 × 10^38  
double: 5.0 × 10^-324 to 1.7 × 10^308

float 是 32 位数字,double 是 64 位数字。

双击新按钮以获取代码。将以下三行添加到您的按钮代码中:

double myDouble;
myDouble = 0.007;
MessageBox.Show(myDouble.ToString());

暂停您的程序并返回到编码窗口。更改此行:

myDouble = 0.007;
myDouble = 12345678.1234567;

运行您的程序并单击您的双按钮。消息框正确显示数字。但是,在末尾添加另一个数字,C# 将再次向上或向下舍入。寓意是,如果您想要准确,请小心舍入!

【讨论】:

你所说的“点东西”一般是指数字的“小数部分”。 “浮点数”并不意味着“一个以点结尾的数字”;而是“浮点”区分数字的类型,而不是“定点”数字(也可以存储小数值);区别在于精度是固定的还是浮动的。 -- 浮点数以精度为代价为您提供更大的动态值范围(最小值和最大值),而定点数以范围为代价为您提供恒定的精度。【参考方案9】:
    Double 和 float 可以除以整数零,在编译和运行时均无例外。 十进制不能被整数零除。如果你这样做,编译总是会失败。

【讨论】:

他们当然可以!它们还有一些“神奇”值,例如 Infinity、Negative Infinity 和 NaN(不是数字),这对于在计算斜率时检测垂直线非常有用……此外,如果您需要在调用 float 之间做出决定.TryParse、double.TryParse 和 decimal.TryParse(例如,检测字符串是否为数字),我建议使用 double 或 float,因为它们会正确解析“Infinity”、“-Infinity”和“NaN” ,而小数则不会。 编译 仅在您尝试将文字 decimal 除以零 (CS0020) 时才会失败,整数文字也是如此。但是,如果运行时十进制值除以零,您将得到异常而不是编译错误。 @BrainSlugs83 但是,您可能不想根据上下文解析“Infinity”或“NaN”。如果开发人员不够严谨,这似乎是用户输入的一个很好的利用。【参考方案10】: 浮点数:±1.5 x 10^-45 到 ±3.4 x 10^38(~7 个有效数字 double:±5.0 x 10^-324 到 ±1.7 x 10^308(15-16 位有效数字) 十进制:±1.0 x 10^-28 到 ±7.9 x 10^28(28-29 位有效数字)

【讨论】:

区别不仅仅是精度。 -- decimal 实际上是以十进制格式存储的(与以 2 为底的格式相反;因此它不会因为两个数字系统之间的转换而丢失或舍入数字);此外,decimal 没有特殊值的概念,例如 NaN、-0、∞ 或 -∞。【参考方案11】:

Decimal、Double 和 Float 变量类型在存储值的方式上有所不同。精度是主要区别,其中 float 是单精度(32 位)浮点数据类型,double 是双精度(64 位)浮点数据类型,decimal 是 128 位浮点数据类型。

浮点数 - 32 位(7 位)

双 - 64 位(15-16 位)

十进制 - 128 位(28-29 位有效数字)

更多关于...the difference between Decimal, Float and Double

【讨论】:

有人知道为什么每种类型都有这些不同的数字范围吗?【参考方案12】:

这对我来说是一个有趣的话题,因为今天,我们刚刚遇到了一个令人讨厌的小错误,涉及 decimal 的精度低于 float

在我们的 C# 代码中,我们从 Excel 电子表格中读取数值,将它们转换为 decimal,然后将此 decimal 发送回服务以保存到 SQL Server 数据库中.

Microsoft.Office.Interop.Excel.Range cell = …
object cellValue = cell.Value2;
if (cellValue != null)

    decimal value = 0;
    Decimal.TryParse(cellValue.ToString(), out value);

现在,对于我们的几乎所有 Excel 值,这非常有效。但是对于一些非常小的 Excel 值,使用 decimal.TryParse 完全失去了值。一个这样的例子是

cellValue = 0.00006317592

Decimal.TryParse(cellValue.ToString(), out value); // 将返回 0

奇怪的是,解决方案是先将 Excel 值转换为double,然后再转换为decimal

Microsoft.Office.Interop.Excel.Range cell = …
object cellValue = cell.Value2;
if (cellValue != null)

    double valueDouble = 0;
    double.TryParse(cellValue.ToString(), out valueDouble);
    decimal value = (decimal) valueDouble;
    …

尽管double 的精度低于decimal,但这实际上确保了仍然可以识别小数字。出于某种原因,double.TryParse 实际上能够检索到如此小的数字,而 decimal.TryParse 会将它们设置为零。

奇怪。很奇怪。

【讨论】:

出于好奇,cellValue.ToString() 的原始值是多少? Decimal.TryParse("0.00006317592", out val) 似乎工作...... -1 不要误会我的意思,如果是真的,这很有趣,但这是一个单独的问题,它肯定不是这个问题的答案。 可能是因为 Excel 单元格返回双精度值,而 ToString() 值为“6.31759E-05”,因此 decimal.Parse() 不喜欢这种表示法。我敢打赌,如果你检查了 Decimal.TryParse() 的返回值,它会是假的。 @weston Answers 通常通过填写他们遗漏的细微差别来补充其他答案。这个答案突出了解析方面的差异。这在很大程度上是对问题的回答! 呃...decimal.Parse("0.00006317592") 有效——你还有其他事情要做。 -- 可能是科学记数法?【参考方案13】:

对于内存和性能都至关重要的游戏和嵌入式系统等应用程序,浮点数通常是首选的数字类型,因为它速度更快,大小只有双精度数的一半。整数曾经是首选的武器,但浮点性能在现代处理器中已经超过了整数。小数点就出来了!

【讨论】:

几乎所有现代系统,甚至是手机,都有对双倍的硬件支持;如果您的游戏甚至具有简单的物理特性,您会注意到 double 和 float 之间的巨大差异。 (例如,在一个简单的小行星克隆中计算速度/摩擦力,双打允许加速度流动比浮动更流畅。- 似乎它不应该重要,但它完全可以。) 双倍也是浮点数的两倍,这意味着您需要咀嚼两倍的数据,这会损害您的缓存性能。与往常一样,衡量并相应地进行。【参考方案14】:

十进制 128 位(28-29 位有效数字) 在金融应用的情况下,最好使用 小数类型,因为它可以为您提供高水平的准确性并且易于避免舍入错误 对需要精度的非整数数学使用小数(例如货币和货币)

64 位(15-16 位) Double 类型可能是实际值最常用的数据类型,除了处理钱。 对不需要最精确答案的非整数数学使用 double。

浮点数 32 位(7 位) 它主要用于图形库,因为对处理能力的要求非常高, 还使用了可以忍受舍入误差的情况。

Decimalsdouble/float 慢得多。

DecimalsFloats/Doubles 不能在没有强制转换的情况下进行比较,而 FloatsDoubles 可以。

Decimals 也允许编码或尾随零。

【讨论】:

【参考方案15】:

所有这些类型的问题是存在一定的不精确性 并且这个问题可能会出现在以下示例中的小十进制数字中

Dim fMean as Double = 1.18
Dim fDelta as Double = 0.08
Dim fLimit as Double = 1.1

If fMean - fDelta < fLimit Then
    bLower = True
Else
    bLower = False
End If

问题:bLower 变量包含哪个值?

答案:在 32 位机器上 bLower 包含 TRUE !!!

如果我将 Double 替换为 Decimal,则 bLower 包含 FALSE,这是一个很好的答案。

在 double 中,问题在于 fMean-fDelta = 1.09999999999 低于 1.1。

注意:我认为其他数字肯定存在同样的问题,因为 Decimal 只是精度更高的双精度数,并且精度总是有限制的。

其实Double、Float和Decimal对应COBOL中的BINARY十进制!

很遗憾,在.Net 中不存在其他用COBOL 实现的数值类型。对于不了解COBOL的,COBOL中存在以下数字类型

BINARY or COMP like float or double or decimal
PACKED-DECIMAL or COMP-3 (2 digit in 1 byte)
ZONED-DECIMAL (1 digit in 1 byte) 

【讨论】:

【参考方案16】:

简单来说:

    Decimal、Double 和 Float 变量类型在存储值的方式上有所不同。 精度是主要区别(请注意,这不是唯一区别),其中 float 是单精度(32 位)浮点点数据类型,double 是双精度(64 位)浮点数据类型,decimal 是 128 位浮点数据类型。 汇总表:

/==========================================================================================
    Type       Bits    Have up to                   Approximate Range 
/==========================================================================================
    float      32      7 digits                     -3.4 × 10 ^ (38)   to +3.4 × 10 ^ (38)
    double     64      15-16 digits                 ±5.0 × 10 ^ (-324) to ±1.7 × 10 ^ (308)
    decimal    128     28-29 significant digits     ±7.9 x 10 ^ (28) or (1 to 10 ^ (28)
/==========================================================================================
您可以阅读更多 here、Float、Double 和 Decimal。

【讨论】:

此答案添加了哪些现有答案尚未涵盖的内容?顺便说一句,您在“十进制”行中的“或”不正确:您要复制的网页中的斜杠表示除法而不是替代。 我强烈反对精度是主要区别。主要区别在于基数:十进制浮点与二进制浮点。这种差异使Decimal 适合金融应用程序,它是在DecimalDouble 之间做出决定时使用的主要标准。例如,Double 精度不足以满足科学应用的情况很少见(而 Decimal 由于范围有限,通常不适合科学应用)。【参考方案17】:

它们之间的主要区别在于精度。

float32-bit 号码,double64-bit 号码,decimal128-bit 号码。

【讨论】:

【参考方案18】:

在 .Net (c#) 中定义 Decimal、Float 和 Double

您必须将值提及为:

Decimal dec = 12M/6;
Double dbl = 11D/6;
float fl = 15F/6;

并检查结果。

每个占用的字节数是

Float - 4
Double - 8
Decimal - 12

【讨论】:

以上是关于.NET 中十进制、浮点和双精度的区别?的主要内容,如果未能解决你的问题,请参考以下文章

单精度和双精度浮点值有啥不同? [复制]

在 .net 中,如何在十进制和双精度之间进行选择

在 .net 中,如何在十进制和双精度之间进行选择

浮点型是啥意思

IEEE-754浮点标准简介

double精确到几位小数