设为首页收藏本站Access中国

Office中国论坛/Access中国论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

123下一页
返回列表 发新帖
查看: 6730|回复: 21
打印 上一主题 下一主题

[Access本身] 【原创 / 文章】谨慎使用单精度/双精度数值类型

[复制链接]
跳转到指定楼层
1#
发表于 2005-9-5 21:39:00 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
前言

  在近日几个帖子里面,和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
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 分享淘帖 订阅订阅
22#
发表于 2009-10-24 11:14:11 | 只看该作者
谢谢分享,学习学习。
21#
发表于 2009-3-6 15:57:19 | 只看该作者
为什么双精度型字段求和后,出现很多位的小数啊?
20#
发表于 2007-1-16 20:18:00 | 只看该作者
碰到过同类问题,谢谢楼主讲解!
19#
发表于 2007-1-14 00:43:00 | 只看该作者
ding
18#
发表于 2006-8-16 21:18:00 | 只看该作者
真是好东西,版主花了不少时间啊!

点击这里给我发消息

17#
发表于 2006-8-8 22:19:00 | 只看该作者
原来如此,太好了
16#
发表于 2006-8-4 19:52:00 | 只看该作者
佩服啊!!!!!!!!!!!!!!!!!!!!1
15#
发表于 2006-1-27 22:51:00 | 只看该作者
不看不知道,一看吓一跳

14#
发表于 2005-9-9 06:29:00 | 只看该作者
不错,精品
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|站长邮箱|小黑屋|手机版|Office中国/Access中国 ( 粤ICP备10043721号-1 )  

GMT+8, 2025-1-11 07:52 , Processed in 0.103005 second(s), 34 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表