`
devgis
  • 浏览: 134083 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

浮点数的误差问题

 
阅读更多

一直很奇怪C#的预定义数据类型中为什么加了一个decimal,有floatdouble不就够了吗?今天来挖一挖。

浮点型

Name

CTS Type

Description

Significant Figures

Range (approximate)

float

System.Single

32-bit single-precision floating point

7

±1.5 × 10−45 to ±3.4 × 1038

double

System.Double

64-bit double-precision floating point

15/16

±5.0 × 10 −324 to ±1.7 × 10308

如果我们在代码中写一个12.3,编译器会自动认为这个数是个double型。所以如果我们想指定12.3float类型,那么你必须在数字后面加上F/f
float f = 12.3F;

decimal类型

作为补充,decimal类型用来表示高精度的浮点数

Name

CTS Type

Description

Significant Figures

Range (approximate)

decimal

System.Decimal

128-bit high precision decimal notation

28

±1.0 × 10−28 to ±7.9 × 1028

从上表可以看出,decimal的有效位数很大,达到了28位,但是表示的数据范围却比floatdouble类型小。decimal类型并不是C#中的基础类型,所以使用的时候会对计算时的性能有影响。

decimalfloatdouble运算结果出现误差的分析

我们知道将一个十进制数值转换为二进制数值,需要通过下面的计算方法:

1. 整数部分:连续用该整数除以2,取余数,然后商再除以2,直到商等于0为止。然后把得到的各个余数按相反的顺序排列。简称"2取余法"

2. 小数部分:十进制小数转换为二进制小数,采用"2取整,顺序排列"法。用2乘以十进制小数,将得到的整数部分取出,再用2乘余下的小数部分,然后再将积的整数部分取出,如此进行,直到积中的小数部分为0或者达到所要求的精度为止。然后把取出的整数部分按顺序排列起来,即先取出的整数部分作为二进制小数的高位,后取出的整数部分作为低位有效位。简称"2取整法"

3. 含有小数的十进制数转换成二进制,整数、小数部分分别进行转换,然后相加。

例如:将十进制数值25.75转换为二进制数值,步骤如下:

25(整数部分)

25/2=12......1

12/2=6.......0

6/2=3......0

3/2=1......1

1/2=0......1

(25) 10=(11001) 2

0.75(小数部分)

0.75*2=1.5......1

0.5*2=1......1

(0.75) 10=(0.11) 2

(25.75) 10=(11001) 2+(0.11)2=(11001.11) 2

按照上述方法,我们将0.650.6转换为二进制代码:

(0.65)10 =(0.101001100110011001100110011001100110011......)2

(0.6) 10 =(0.10011001100110011001100110011001100110011......)2

后面的省略号表示已经算不完了,后面在无限重复 0011这段二进制数值。

文章开始部分,我们用的float类型,下面我们来看看float类型是否能存储上面转换出的二进制代码。

目前计算机上存储浮点数值是按照IEEE(电气和电子工程师协会)754浮点存储格式标准来存储的。

IEEE单精度浮点格式共32位,包含三个构成字段:23位小数f8位偏置指数e1位符号s。将这些字段连续存放在一个32位字里,并对其进行编码。其中0:22位包含23位的小数f 23:30位包含8位指数e;第31位包含符号s。如下图所示:

也就是说上面将0.650.5转换出的二进制代码,我们只能存储23位,即使数据类型为double,也只能存储52位,这样大家便能看出问题出现的原因了。

截取的二进制代码已无法正确表示0.650.5,根据这个二进制代码肯定无法正确得到结果0.05

decimalfloatdouble错误的认识

引用自:http://topic.csdn.net/t/20050514/20/4007155.htmlIvony的评论

在精确计算中使用浮点数是非常危险的,尽管C#在浮点数运算时采取了很多措施使得浮点数运算的结果看起来是非常正常的。但实际上如果不清楚浮点数的特性而贸然使用的话,将造成非常严重的隐患。

考虑下面的语句:
double dd = 10000000000000000000000d;
dd += 1;
Console.WriteLine ( "{0:G50}", dd );
输出是什么?谁知道?
输出是:1000000000000000000000000
这就是浮点数精度损失的问题,最重要的是,在精度损失的时候,不会报告任何的错误,也不会有任何的异常产生。
浮点数的精度损失可能在很多地方出现,例如d * g / g不一定等于dd / g * g也不一定等于d
还有两个非常危险的错误认识!!
1
decimal不是浮点型、decimal不存在精度损失。下面有段程序大家可以去看看结果是什么。记住!所有的浮点型变量都存在精度损失的问题,而decimal是一个不折不扣的浮点型,不论它精度有多高,精度损失依然存在decimal dd = 10000000000000000000000000000m;
dd += 0.1m;
Console.WriteLine ( "{0:G50}", dd );

2decimal所能储存的数比double大,从doubledecimal的类型转换不会出现任何问题。微软在decimal的帮助上真的要好好反省了。实际上只有从整形到decimal的转换才是扩大转换,decimal的精度比double大,但所能储存的最大数却比double要小。



计算机中的二进制的小数同十进制一样,比如10.125可以写成1.125*10的一次方,二进制写成1010.001,1.010001*2的三次方,都可以写成谁乘以进制的指数次,所以,在IEEE规则中规定浮点数的构成是符号位S+指数位E+尾数M,这个位数的组合构成浮点数。

JAVA遵守IEEE754规则:

float占4个字节,32位,表示为SEEEEEEEEMMMMMMMMMMMMMMMMMM,1位符号位,8位指数位,23位尾数位。当然如果指数值在1-254之间时,为1.M,否则就是0.M,指数又叫阶码,阶码采用移码表示,取值E-127.所以公式可总结为:小于0的小数:(-1)的S次*2的(1-127)次*(0.M),大于0的小数:(-1)的S次*2的(E-127)*(1.M).

5.0的在JAVA中的计算机二进制流表示为:

S=0,

5.0=101=1.01*2的2次

E-127=2 E=129=10000001

M=1.01

组合在一起就是 0 10000001 01000000000000000000000

再转成十进制:(-1)的0次 * 2的(129-127)次*(1.01)=101=5.0

输出float的二进制流,Java中的方法Float.floatToIntBits()

-5.0的表示:

Java二进制流:1 10000001 01000000000000000000000,其他省略。

0.1的表示:

0.1约等于0.0001100110011001100110011001=1.100110011001100110011001*2的-4次

S=0

E-127=-4 E=123=01111011

M=10011001100110011001100

组合在一起在JAVA中二进制流就是0 01111011 100110011001100110011001100

在转换成10进制为0.100000001490116119384765625

误差为0.000000001490116119384765625 如果26个float的0.1相加,结果为2.5999997 如果定义26个double的0.1相加,结果为2.600000000000001.

所以可以得出:

1.浮点数强制转换成整数时,舍入误差严重加重,强制转换时会舍弃非整数部分。

double d=29*0.01;//d=0.29

d=((int)d*100);//d=28

2.科学计数表示法中二进制位数越多,精度就越高。64位double就比32位的float精度高。对精度要求非常高的场合可以用BigDecimal类,能表示任意精度的数。

3.指数E为255时表示特殊数字。


分享到:
评论

相关推荐

    浮点数计算与误差分析

    深入介绍浮点数的存储形式,讲解误差形成的缘由!新的层面,新的认知!!

    自己写的浮点数计算误差分析

    自己写的一篇分析文章,系统地讲述了计算机浮点计算的误差来源并提供一些避免方法。

    单精度浮点数累加和误差研究 (2013年)

    计算机中进行浮点数加法运算时,需要进行对阶和右规格化操作,该操作会进行舍入处理,这种处理过程会产生误差,浮点数累加运算会造成误差的累积,导致计算结果精度不够甚至计算结果错误。通过实验手段研究单精度...

    行业分类-设备装置-一种误差平坦的浮点数对数运算装置.zip

    行业分类-设备装置-一种误差平坦的浮点数对数运算装置.zip

    仪表设计中的浮点数问题及解析

     1 精度问题  所谓标准值是度比被测仪表高3~5倍的标准表测得的数值。仪表精度不仅和误差有关,而且和仪表的测量范围有关。误差大,相对百分误差就大,仪表度就低。如果误差相同的两台仪表,其测量范围不同,那么...

    JavaScript解决浮点数计算不准确问题的方法分析

    主要介绍了JavaScript解决浮点数计算不准确问题的方法,结合实例形式分析了javascript浮点数运算精度误差的原因以及相关的解决方法与具体操作技巧,需要的朋友可以参考下

    IEE754_浮点数编码器

    浮点数编码工具 十进制小数:1.2 十六进制编码:3f 99 99 9a 二进制编码:0011 1111 1001 1001 1001 1001 1001 1010 二进编码真实结果: 1.2000000476837158 可以查看浮点数输入结果和实际结果误差。

    FloatingPointComparison:C ++实现比较两个浮点数

    浮点比较比较浮点数的功能。 计算机中的数字在内部用二进制表示,并且有些浮点数(例如0.1)没有用二进制... 在这里,我们实现了一个函数,该函数通过使用考虑了浮点不精确度的epsilon值(误差容限)来比较两个浮点数。

    关于Python中浮点数精度处理的技巧总结

    而python是以双精度(64)位来保存浮点数,多余的位会被截掉,所以看到的是0.1,但在电脑上实际保存的已不是精确的0.1,参与运算后,也就有可能点误差,特别是金融邻域里面,对精度更是要求更高,如何在Python中获取...

    基于C++浮点数(float、double)类型数据比较与转换的详解

    浮点数在内存中的存储机制和整型数不同,其有舍入误差,在计算机中用近似表示任意某个实数。具体的说,这个实数由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2)的整数次幂得到,这种表示方法类似于...

    Python如何执行精确的浮点数运算

    你需要对浮点数执行精确的计算操作,并且不希望有任何小误差的出现。 解决方案 浮点数的一个普遍问题是它们并不能精确的表示十进制数。 并且,即使是最简单的数学运算也会产生小的误差,比如: >>> a = 4.2 >>> b ...

    c# 定点数库演示,c#和C++有什么差别.docx

    在C#中,使用浮点数进行数学运算时,可能会因为精度问题导致结果不准确。这是因为浮点数的精度受到计算机二进制表示的限制,会产生舍入误差。为了解决这个问题,可以使用定点数进行数学运算。 定点数是一种用整数...

    浮点算术须知(英文)

    Goldburg作于1991年,介绍浮点数的舍入,误差分析,IEEE标准以及定理等等。

    Java实现的浮点类的精确计算

    这个类可以当做一个工具类来用,有了这个工具类,以后的浮点数的计算误差就不再是问题了。

    golang如何比较浮点数的大小

    Golang浮点数比较和运算会出现误差。 浮点数储存至内存中时,2的-1、-2……-n次方不能精确的表示小数部分,所以再把这个数从地址中取出来进行计算就出现了偏差。 package main import ( errors fmt github....

    基于Verilog的单精度浮点数乘法器的设计与实现 (2009年)

    文章详细介绍了浮点数和浮点数乘法的原理,采用Verilog语言设计32位单精度浮点数乘法器。用Modelsim6.5进行了...将仿真结果转换为实数,与期望(真值)相比计算出乘法器的计算误差率,从而验证该设计的正确性和可行性。

    python 浮点数四舍五入需要注意的地方

    本文主要分享基于python的数据分析三方库pandas,numpy的一次爬坑经历,发现并分析了python语言对于浮点数精度处理不准确的问题,并在最后给出合理的解决方案。如果你也在用python处理数据,建议看一下,毕竟0.1的...

    计算机浮点数算术运算的舍入误差研究 (2005年)

    对计算机浮点数算术运算的舍入误差进行分析,是对数值计算方法作误差分析的基础。论文全面研究了计算机浮点数算术运算的舍入误差的基本理论,对有关结论均作了严格的论证。并举例说明由算术运算的误差界,可建立较...

    CalcEval科学计算

    CalcEval引擎是一个专门解决javascript浮点数误差的的引擎,能够完美的解决各种复合的运算,最终输出正确的结果。

Global site tag (gtag.js) - Google Analytics