.NET中小数,浮点数和双精度之间的区别?


Answers:


2265

floatdouble浮点型二进制点类型。换句话说,它们代表这样的数字:

10001.10010110011

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

decimal浮点小数点类型。换句话说,它们代表这样的数字:

12345.65789

同样,小数点的数字和位置都被编码在该值之内,这decimal仍然是浮点类型而不是定点类型。

需要注意的重要一点是,人类习惯于以十进制形式表示非整数,并期望以十进制表示形式得到准确的结果。并非所有十进制数字都可以在二进制浮点数中精确表示(例如0.1),因此,如果您使用二进制浮点数,则实际上将近似为0.1。当使用浮点小数点时,您仍然会得到近似值-例如,不能精确表示1除以3的结果。

至于什么时候使用:

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

  • 如果读入的性质的更加伪影不能真正测量值准确反正float/ double更合适。例如,科学数据通常以这种形式表示。在这里,原始值一开始就不会是“十进制精度”,因此对于预期结果而言,保持“十进制精度”并不重要。浮点二进制点类型比十进制小得多。


58
float/ double平时并不代表数字作为101.101110,通常它被表示为类似1101010 * 2^(01010010)-指数
明威塞缪尔

79
@Hazzard:这就是答案的“和二进制点的位置”部分的意思。
乔恩·斯基特

112
我很惊讶它还没有被说出来,它float是一个C#别名关键字,并且不是.Net类型。它是System.Single.. single并且double是浮动二进制点类型。
布雷特·卡斯威尔

54
@BKSpurgeon:嗯,只能以与您可以说一切都是二进制类型的方式相同,在这种情况下,它变成一个相当无用的定义。十进制是十进制类型,因为它是一个表示为整数有效位数和小数位数的数字,因此结果为有效位数* 10 ^小数位数,而float和double是有效位数* 2 ^小数位数。取一个用十进制表示的数字,然后将小数点向右移动足够远,以得到一个整数以计算出有效位数和小数位数。对于float / double,您将从以二进制形式写的数字开始。
乔恩·斯凯特

21
另一个区别是:float 32位;双64位; 和十进制128位。
大卫,

1072

精度是主要区别。

浮点数 -7位数字(32位)

-15-16位数字(64位)

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

小数位数具有更高的精度,通常在要求高度准确性的金融应用程序中使用。小数要比double / float慢得多(在某些测试中,多达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

65
@Thecrocodilehunter:对不起,但是没有。小数可以表示可以用十进制表示法表示的所有数字,例如不能表示1/3。1.0m / 3.0m的取值为0.33333333 ...,但末尾有3s,但数量有限。将其乘以3将不会返回精确的1.0。
Erik P.

50
@Thecrocodilehunter:我认为您混淆了准确性和精确性。在这种情况下,它们是不同的事物。精度是可用来表示数字的位数。精度越高,您需要舍入的次数就越少。没有数据类型具有无限的精度。
Igby Largeman'1

13
@Thecrocodilehunter:您假设要测量的值是准确的 0.1 -在现实世界中很少有这种情况! 任何有限的存储格式都会将无限数量的可能值与有限数量的位模式混淆。例如,float将合并0.10.1 + 1e-8,而decimal将合并0.10.1 + 1e-29。当然,在给定范围内,某些值可以以零损失的精度以任何格式表示(例如,float可以以零损失的精度存储高达1.6e7的任何整数)-但这仍然不是无限的精度。
丹尼尔·普赖登

27
@Thecrocodilehunter:你错过了我的观点。 0.1不是一个特殊的价值!唯一使之0.1比“更好”的0.10000001是,因为人类喜欢以10为底。即使使用一个float值,如果您以0.1相同的方式初始化两个值,它们将都是相同的值。只是该值不会完全相同 0.1 -它是最接近的值0.1,可以精确地表示为float。当然,使用二进制浮点数,(1.0 / 10) * 10 != 1.0但也使用十进制浮点数(1.0 / 3) * 3 != 1.0两者都不完全精确的。
Daniel Pryden 2012年

16
@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也将是如此。但是,在这种情况下,既不a,也不b正好等于1/3-他们都将相等0.3333...。在这两种情况下,都会由于表示而损失一些准确性。您固执地说这decimal具有“无限”的精度,这是错误的
Daniel Pryden 2012年

84

十进制结构严格适合需要准确性的财务计算,而这些准确性相对不能四舍五入。但是,由于以下几个原因,十进制数不足以用于科学应用:

  • 由于许多物理问题或被测量工件的实际限制,在许多科学计算中都可能会损失一定的精度。精度损失在金融中是不可接受的。
  • 对于大多数操作,小数比浮点和双精度慢很多(很多),主要是因为浮点运算是在二进制中完成的,而小数填充是在基数10中完成的(即浮点和双精度是由FPU硬件处理的,例如MMX / SSE) ,而小数则由软件计算得出)。
  • 尽管十进制支持更多位数的精度,但其取值范围小于双精度数是不可接受的。因此,十进制不能用来表示许多科学价值。

5
如果您要进行财务计算,则绝对必须使用自己的数据类型或找到满足您确切需求的良好库。财务环境中的准确性由(人类)标准机构定义,并且它们具有关于如何进行计算的非常特定的本地化规则(在时间和地理方面)。.Net中的简单数字数据类型未捕获诸如正确舍入之类的内容。进行计算的能力只是难题的一小部分。
詹姆斯·摩尔

76
+---------+----------------+---------+----------+---------------------------------------------+
| C#      | .Net Framework | Signed? | Bytes    | Possible Values                             |
| Type    | (System) type  |         | Occupied |                                             |
+---------+----------------+---------+----------+---------------------------------------------+
| sbyte   | System.Sbyte   | Yes     | 1        | -128 to 127                                 |
| short   | System.Int16   | Yes     | 2        | -32768 to 32767                             |
| int     | System.Int32   | Yes     | 4        | -2147483648 to 2147483647                   |
| long    | System.Int64   | Yes     | 8        | -9223372036854775808 to 9223372036854775807 |
| byte    | System.Byte    | No      | 1        | 0 to 255                                    |
| ushort  | System.Uint16  | No      | 2        | 0 to 65535                                  |
| uint    | System.UInt32  | No      | 4        | 0 to 4294967295                             |
| ulong   | System.Uint64  | No      | 8        | 0 to 18446744073709551615                   |
| float   | System.Single  | Yes     | 4        | Approximately ±1.5 x 10-45 to ±3.4 x 1038   |
|         |                |         |          |  with 7 significant figures                 |
| double  | System.Double  | Yes     | 8        | Approximately ±5.0 x 10-324 to ±1.7 x 10308 |
|         |                |         |          |  with 15 or 16 significant figures          |
| decimal | System.Decimal | Yes     | 12       | Approximately ±1.0 x 10-28 to ±7.9 x 1028   |
|         |                |         |          |  with 28 or 29 significant figures          |
| char    | System.Char    | N/A     | 2        | Any Unicode character (16 bit)              |
| bool    | System.Boolean | N/A     | 1 / 2    | true or false                               |
+---------+----------------+---------+----------+---------------------------------------------+

有关更多信息,请参见此处


5
您没有留出最大的差异,它是十进制类型使用的基数(十进制存储为10的底数,列出的所有其他数字类型均为2的底数)。
BrainSlugs83 2015年

1
上图或源论坛帖子中未正确显示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

2
十进制是128位...表示它占用16个字节而不是12个
user1477332

51

我不会重复在其他答案和评论中已经回答过的大量好(和坏)信息,但是我将向您提示一些后续问题:

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

使用十进制数值

将浮点/双精度用作测量

一些例子:

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

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

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

我们总是数钱,永远不要衡量。我们通常测量距离。我们经常计算分数。

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


1
我真的很喜欢这个答案,特别是“我们数钱还是数钱?”这个问题。但是,除了金钱,我想不出任何“计数”的东西,而不仅仅是整数。我看到一些使用十进制的应用程序仅仅是因为double的有效数字太少了。换句话说,可以使用十进制,因为C#没有四重类型en.wikipedia.org/wiki/Quadruple-precision_floating-point_format
John Henckel

48

float 7位精度

double 大约有15位数字的精度

decimal 约有28位精度

如果需要更高的精度,请使用double而不是float。在现代CPU中,两种数据类型几乎都具有相同的性能。使用float的唯一好处是它们占用的空间更少。实际上只有在您有很多的情况下才重要。

我发现这很有趣。每位计算机科学家都应了解的浮点运算法则


1
@RogerLipscombe:double在没有大于32位整数类型可用的情况下(并且基本上只在那些情况下),并且在double使用时就好像它是53位整数类型(例如用于保留),我认为在会计应用程序中是合适的便士的总数,或百分之一的总数)。如今,此类语言用途不大,但是许多语言早在获得64位(甚至在某些情况下甚至是32位!)整数数学之前就已经能够使用双精度浮点值。
超级猫

1
您的答案暗示精度是这些数据类型之间的唯一区别。给定二进制浮点算法通常是在硬件FPU实现的,因此性能存在显着差异。这对于某些应用程序可能无关紧要,但对于其他应用程序则至关重要。
Saille

6
@supercat double 在会计应用程序中永远不合适。因为Double只能近似十进制值(即使在其自身精度范围内)。这是因为double以以2为底的(二进制)中心格式存储值。
BrainSlugs83 2015年

2
@ BrainSlugs83:使用浮点类型来保存非整数数量是不适当的,但是从历史上看,语言具有浮点类型可以精确表示比整数类型更大的整数值是很常见的。也许最极端的例子是Turbo-87,它的整数类型仅限于-32768到+32767,但是RealIIRC可以以单位精度​​表示高达1.8E + 19的值。我认为,Real比起…… ,会计应用程序要代表几分
钱就更明智了

1
...以便它尝试使用一堆16位值执行多精度数学运算。对于大多数其他语言,这种差异并不是那么严重,但是很长一段时间以来,对于语言而言,不具有超出4E9的任何整数类型而是具有double单位精度高达9E15 的类型是非常普遍的。如果需要存储大于最大可用整数类型的整数,则使用数字double比尝试进行多精度数学运算更容易,更有效,特别是考虑到处理器具有执行16x16-> 32 or的指令。 ..
超级猫

36

没有人提到

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

我的意思是

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

抛出OverflowException

但是这些不:

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

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

1
float.MaxValue+1 == float.MaxValue,就像decimal.MaxValue+0.1D == decimal.MaxValue。也许您的意思是float.MaxValue*2
2015年

@supercar但是,并非十进制.MaxValue + 1 ==十进制.MaxValue
GorkemHalulu 2015年

@supercar decimal.MaxValue + 0.1m == decimal.MaxValue正常
GorkemHalulu 2015年

1
System.Decimal抛出之前变得无法分辨整个单元的异常,但如果一个应用程序应该能够处理如美元和美分,这可能是太晚了。
supercat 2015年

28
  1. Double和float可以被整数零除,在编译和运行时均无例外。
  2. 小数不能除以整数零。如果这样做,编译将始终失败。

6
他们肯定可以!它们还具有几个“魔术”值,例如Infinity,Negative Infinity和NaN(非数字),这对于在计算斜率时检测垂直线非常有用...此外,如果需要在调用float之间进行选择.TryParse,double.TryParse和decimal.TryParse(例如,检测字符串是否为数字),我建议使用double或float,因为它们会正确解析“ Infinity”,“-Infinity”和“ NaN” ,而十进制则不会。
BrainSlugs83 2011年

仅当尝试将文字decimal除以零(CS0020)时,编译才会失败,并且整数文字也是如此。但是,如果运行时十进制值除以零,则会得到异常,而不是编译错误。
德鲁·诺阿克斯

@ BrainSlugs83但是,根据上下文,您可能不想解析“ Infinity”或“ NaN”。如果开发人员不够严谨,似乎可以很好地利用用户输入。

28

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

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#将再次向上或向下取整。道德是如果您想准确性,请注意四舍五入!


2
您提到的“指向某物”通常称为数字的“小数部分”。“浮点数”并不表示“末尾带有数字的数字”;但是相反,“浮点数”区分数字的类型,而不是“定点数”(也可以存储小数)。区别在于精度是固定的还是浮动的。-浮点数为您提供更大的动态范围值(最小值和最大值),而以精度为代价,而定点数为您提供了恒定的精度,而代价是范围。
BrainSlugs83

16
  • 浮点数:±1.5 x 10 ^ -45至±3.4 x 10 ^ 38(〜7位有效数字
  • 两倍:±5.0 x 10 ^ -324至±1.7 x 10 ^ 308(15-16位有效数字)
  • 十进制:±1.0 x 10 ^ -28至±7.9 x 10 ^ 28(28-29有效数字)

9
区别不只是精度。- decimal实际上是以十进制格式存储的(相对于基数2;因此不会由于两个数字系统之间的转换而丢失或舍入数字);此外,decimal没有特殊值的概念,例如NaN,-0,∞或-∞。
BrainSlugs83

13

对于我来说,这是一个有趣的话题,因为今天,我们遇到了一个讨厌的小错误,涉及到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(),输出值);//将返回0

奇怪的是,解决方案是double先将Excel值转换为,然后转换为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精度不如a decimal,这实际上确保了仍然可以识别出少量数字。由于某种原因,double.TryParse实际上能够检索到如此小的数字,而decimal.TryParse会将它们设置为零。

奇。很奇怪。


3
出于好奇,cellValue.ToString()的原始值是什么?Decimal.TryParse(“ 0.00006317592”,val)似乎可以工作...
micahtan,2012年

11
-1不要误会我的意思,如果是真的,这很有趣,但这是一个单独的问题,它肯定不是该问题的答案。
weston,

2
可能是因为Excel单元格返回了double,并且ToString()的值为“ 6.31759E-05”,所以decimal.Parse()不喜欢这种表示法。我敢打赌,如果您检查了Decimal.TryParse()的返回值,那将是错误的。
SergioL 2014年

2
@weston答案通常通过填写他们错过的细微差别来补充其他答案。该答案突出说明了解析方面的差异。这是对这个问题的答案!
罗宾诺

1
嗯... decimal.Parse("0.00006317592")有效-您还有其他事情要发生。-可能是科学记数法吗?
BrainSlugs83

8

对于诸如内存和性能都至关重要的游戏和嵌入式系统之类的应用程序,浮点数通常是首选的数字类型,因为它速度更快,是两倍大小的一半。整数曾经是首选武器,但浮点性能已超过现代处理器中的整数。十进制就正确了!


几乎所有现代系统,甚至包括手机,都具有两倍的硬件支持。如果您的游戏甚至具有简单的物理原理,您也会注意到double和float的巨大区别。(例如,在一个简单的Asteroids克隆中计算速度/摩擦力,加倍允许加速度比浮动更流畅地流动
。-

双精度也是浮点数的两倍,这意味着您需要读取两倍的数据,这会损害您的缓存性能。与往常一样,进行相应的测量并继续进行。
yoyo

6

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

浮点数-32位(7位数字)

双精度-64位(15-16位)

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

有关... 更多信息... 小数,浮点数和双精度之间的区别


5

所有这些类型的问题都是存在一定的不精确性,并且使用小十进制数可能会出现此问题,如以下示例所示

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,这是一个很好的答案。

双重地,问题在于fMean-fDelta = 1.09999999999,它低于1.1。

注意:我认为其他数字肯定会存在相同的问题,因为十进制只是精度较高的双精度数,精度始终有极限。

实际上,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) 

4

简单来说:

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

/==========================================================================================
    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)
/==========================================================================================
您可以在此处阅读更多信息FloatDoubleDecimal


现有答案尚未涵盖的答案是什么?顺便说一句,“十进制”行中的“或”是不正确的:您要从中复制的网页中的斜杠表示分隔而不是替代。
马克·迪金森

1
我强烈认为精度是主要区别。主要区别在于基数:十进制浮点数与二进制浮点数。这种差异使之Decimal适合于金融应用程序,这是在Decimal和之间确定的主要标准Double。例如,很少有Double精度不足以用于科学应用(并且由于其范围有限,Decimal常常适合科学应用)。
马克·迪金森

2

这些之间的主要区别在于精度。

float是一个32-bit数字,double是一个64-bit数字,decimal也是一个128-bit数字。


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

  • 精度64位(15-16位数字)双精度类型可能是用于实值的最常用数据类型,除了处理资金外。对于不需要最精确答案的非整数数学,请使用double。

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

Decimals比慢得多double/float

Decimals并且Floats/Doubles不能没有投,而相比较Floats,并Doubles可以。

Decimals 还允许编码或尾随零。


-1

在.Net中定义小数,浮点和双精度(c#)

您必须将值提及为:

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

并检查结果。

每个占用的字节是

Float - 4
Double - 8
Decimal - 12
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.