Office中国论坛/Access中国论坛

标题: 【原创 / 文章】谨慎使用单精度/双精度数值类型 [打印本页]

作者: LucasLynn    时间: 2005-9-5 21:39
标题: 【原创 / 文章】谨慎使用单精度/双精度数值类型
前言

  在近日几个帖子里面,和QQ群的讨论里面,我发现很多网友都遇到的问题都是因为不恰当地使用了单精度/双精度数值。因此想专门就这个话题谈一下。

  单精度和双精度数值类型最早出现在C语言中(比较通用的语言里面),在C语言中单精度类型称为浮点类型(Float),顾名思义是通过浮动小数点来实现数据的存储。这两个数据类型最早是为了科学计算而产生的,他能够给科学计算提供足够高的精度来存储对于精度要求比较高的数值。但是与此同时,他也完全符合科学计算中对于数值的观念:

  当我们比较两个棍子的长度的时候,一种方法是并排放着比较一下,一种方法是分别量出长度。但是事实上世界上并不存在两根完全一样长的棍子,我们测量的长度精度受到人类目测能力和测量工具精度的限制。从这个意义上来说,判断两根棍子是否一样长丝毫没有意义,因为结果一定是False,但是我们可以比较他们两个哪个更长或者更短。这个例子很好地概括了单精度/双精度数值类型的设计初衷和存在意义。

  基于上述认识,单精度/双精度数值类型从一开始设计的时候,就不是一个准确的数值类型,他只保证在他这个数值类型的精度之内是准确的,精度之外则不保证,比方说,一个数值5.1,很可能存储在单精度/双精度数值中的实际值是5.100000000001或者5.09999999999999。导致这个现象的原因我们可以通过两种方式来解释:

简单的解释方法:

  你可以尝试在任何一个控件的属性面板中,设定他的宽度为:3.2CM,当你输入完毕后,你会发现值自动变成了3.199cm,无论你怎么改,你都无法输入3.200CM,因为实际上在电脑中存储的并不是CM为单位的数值,而是“缇”为单位的数值,而“缇”和CM之间的比值,是个很难被除尽的数,因此你输入完毕后,电脑自动转换成了最接近的“缇”值,然后再转换成厘米显示到属性面板上,这一乘一除,两次四舍五入,误差就出来了。单精度/双精度也是类似的原理,其实在二进制存储的时候,单精度/双精度都采用了类似相近分数的方法,而这样的存储是不可能做到准确的。

深入的解释方法:

  让我们来看看我们存储到数字介质中的单精度/双精度值到底是怎么样的,我们使用如下代码对单精度类型进行一个解剖:

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)





Public Sub floatTest()
Dim dblVar As Single

dblVar = 5.731 / 8
dblOutput dblVar

dblVar = dblVar * 2
dblOutput dblVar

dblVar = dblVar * 2
dblOutput dblVar

dblVar = dblVar * 2
dblOutput dblVar

dblVar = dblVar * 2
dblOutput dblVar

dblVar = dblVar * 2
dblOutput dblVar

End Sub

Public Sub dblOutput(ByVal dblVar As Single)
    Dim bytVar(3) As Byte
    Dim i As Integer, j As Integer
    Dim strVar As String

    CopyMemory ByVal VarPtr(bytVar(0)), ByVal VarPtr(dblVar), 4
    strVar = dblVar & ": "
    For i = 3 To 0 Step -1
        For j = 7 To 0 Step -1
            strVar = strVar & (bytVar(i) And 2 ^ j) / 2 ^ j
        Next j
        strVar = strVar & " "
    Next i
    Debug.Print strVarEnd Sub

  运行后我们得到输出结果(输出格式为高位左,低位右):

.716375: 00111111 00110111 01100100 01011010
1.43275: 00111111 10110111 01100100 01011010
2.86550: 01000000 00110111 01100100 01011010
5.73100: 01000000 10110111 01100100 01011010
11.4620: 01000001 00110111 01100100 01011010
22.9240: 01000001 10110111 01100100 01011010

  这里,我们把单精度类型转化成了二进制数据输出,这里我们看到,虽然这六个数字完全不同,但是他们的二进制存储惊人地相似,我们看到红色标记部分,每次都是加1,事实上,单精度数据类型使用从高位开始第1位作为正负标记位(绿色),第2位到第9位,是一个跨字节的有符号字节类型数据,这个数值决定了小数点移动的方向和位数(红色),第10
作者: laowu    时间: 2005-9-6 09:49
好文章,顶!
作者: Grant    时间: 2005-9-6 11:46
非常的好,佩服!佩服!看了这文章才知道单/双精度数有此缺陷.
作者: secowu    时间: 2005-9-7 00:32
以下是引用laowu在2005-9-6 1:49:00的发言:

好文章,顶!




作者: wang1950317    时间: 2005-9-7 00:41
高深!
作者: LucasLynn    时间: 2005-9-7 01:02
以下是引用wang1950317在2005-9-6 16:41:00的发言:

高深!



觉得太深你就这样理解:

1/3=0.333333333333

0.333333333333*3=0.999999999999

你输入一个1,电脑除以3给你存储个0.333333333333,然后读取的时候乘以个3显示0.999999999999。误差就出来了。

单精度/双精度的误差都是这样引起的。
作者: LucasLynn    时间: 2005-9-7 01:52
以下是引用Grant在2005-9-6 3:46:00的发言:

非常的好,佩服!佩服!看了这文章才知道单/双精度数有此缺陷.
这个不能说是缺陷,而是特点,就像你不能说无法存储小数是整型的缺陷一样。只是因为很多人并不了解单精度/双精度数据类型的原理,错误使用所以引发了问题,这是使用者的问题。也是我发这个帖子的原因。
作者: secowu    时间: 2005-9-7 19:36
那用什么才能精确呢?比如:我的数据格式有三位的:0.0321.2311.2850.128那么该如何给这个字段设定类型?
作者: LucasLynn    时间: 2005-9-7 20:29
以下是引用secowu在2005-9-7 11:36:00的发言:

那用什么才能精确呢?

比如:我的数据格式有三位的:

0.032

1.231

1.285

0.128

那么该如何给这个字段设定类型?



1、货币类型

2、都乘以1000存储。其实货币类型本身就是浮点整数。

[此贴子已经被作者于2005-9-7 12:29:36编辑过]


作者: xlonger    时间: 2005-9-8 16:30
我将一个字段数据类型设置为单精度后,导出包含该字段的查询为文本文件,发现有小数的字段值,如100.21,变成100.20,各个数据都是少0.01,后来改成双精度,好像再也没有发生该现象。我用的数字都是2位小数,不知道用双精度合适吗?
作者: cg1    时间: 2005-9-8 16:51
楼上的,请认真看楼主的文章
作者: secowu    时间: 2005-9-8 17:07
以下是引用LucasLynn在2005-9-7 12:29:00的发言:







1、货币类型

2、都乘以1000存储。其实货币类型本身就是浮点整数。



然后要调用时,再/1000?
作者: 喳喳林    时间: 2005-9-8 21:20
谢谢,上了节好课。以后如果数据要进行比较的,会谨慎选择,是在以后设计时要防的BUG。谢谢
作者: 李寻欢    时间: 2005-9-9 06:29
不错,精品
作者: sun.gd    时间: 2006-1-27 22:51
不看不知道,一看吓一跳


作者: 古老的龙    时间: 2006-8-4 19:52
佩服啊!!!!!!!!!!!!!!!!!!!!1
作者: t小宝    时间: 2006-8-8 22:19
原来如此,太好了
作者: z00    时间: 2006-8-16 21:18
真是好东西,版主花了不少时间啊!
作者: benjiang    时间: 2007-1-14 00:43
ding
作者: 均瑶    时间: 2007-1-16 20:18
碰到过同类问题,谢谢楼主讲解!
作者: yttzm    时间: 2009-3-6 15:57
为什么双精度型字段求和后,出现很多位的小数啊?
作者: chaojianan    时间: 2009-10-24 11:14
谢谢分享,学习学习。




欢迎光临 Office中国论坛/Access中国论坛 (http://www.office-cn.net/) Powered by Discuz! X3.3