SAS 数值存储方式和精度问题
本文链接:https://www.cnblogs.com/snoopy1866/p/16021137.html
1 数值存储方式
SAS使用8个字节存储数值,使用浮点计数法表示数值。
浮点计数法由4个部分组成:符号位(Sign)、基数(Base)、指数(Exponent)、尾数(Mantissa),这4个部分分别提供以下作用:
- 符号位:代表该数值是正数还是负数
- 基数:SAS系统中的基数默认为2。
- 指数:代表基数被乘的倍数
- 尾数:定义数值范围的一个小数
将计算机内部存储的数值转化为10进制数值的公式为:\(\color{red}{Sign}\)*(\(\color{purple}{Mantissa}\) * Base$\color{green}{Exponent}$)
下图展示了SAS用8个字节表示浮点数的具体情况:

即第1位为符号位,第2-12位为指数位,13-64位为尾数位,符号位中,0表示正数,1表示负数。
例如:Sign = 1, Exponent = 3, Mantissa = 1492,在10进制下代表:(-1) * (0.1492 * 103) = -149.2,在2进制下表示:(-1) * (0.1492 * 22) = -1.1936;
2 产生的问题
不是所有浮点数都能用8个字节精确地表示,某些浮点数甚至无法用有限个字节精确地表示,这在任何进制下都是存在的问题。
例如:10进制下的0.1在16进制下表示为3FB999999999999999999999999999999...,2/3在10进制下表示为0.666666666666666666666...
当计算机面对这种情况时,有两种选择:
(1)Truncation:2/3+2/3+2/3 = 0.666666 + 0.666666 + 0.666666 = 1.999998
(2)Rounding: 2/3+2/3+2/3 = 0.666667 + 0.666667 + 0.666667 = 2.000001
但这两种选择都无法完美解决精度问题,而且随着运算次数的累计,精度问题会愈发凸显。
来看下面一个例子:
/*DO 迭代生成0.1~0.9*/
data s1;
do a = 0.1 to 0.9 by 0.1;
index + 1;
output;
end;
run;
/*手动输入0.1~0.9*/
data s2;
do b = 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9;
index + 1;
output;
end;
run;
/*合并s1-s2*/
data s3;
merge s1 s2;
by index;
if a = b then A_eq_B = "Y";
run;
proc print data = s3;
var index a b A_eq_B;
run;

可以看到PROC PRINT打印出来的结果中显示,A,B两列显示完全一致,但其中0.3,0.8和0.9却出现了A≠B的情况。
为了能够看到SAS内部储存的实际数值,使用十六进制输出格式输出结果:
proc print data = s3;
var index a b A_eq_B;
format a hex16. b hex16.;
run;

由此可见,是DO循环迭代计算导致的误差累计,最终导致A和B两列实际存储的数值存在微小差异。
这种精度损失可能会在下述场景中导致问题:
- 使用 IF,SELECT,WHERE语句比较两个不同计算方法得出的数值,或与显式指定的数值比较时;
- 对数据集取子集时;
- 使用 PROC COMPARE 过程比较两个数据集时
- 对基于计算产生的浮点数进行分类分析时
- PROC REPORT或其他过程步的输出可能会显示出奇怪的小数(例如:-0.00)
3 解决办法
以下3种方式可以解决SAS中存在的大多数精度问题:
- 使用 ROUND 函数
- 为数值变量创建字符串版本的变量
- 利用过程步中的选项
(1)ROUND函数
在不需要特别精确的情况下,可以使用ROUND函数仅比较前几位小数。
data s3;
merge s1 s2;
by index;
a_r = round(a, 0.1);
b_r = round(b, 0.1);
if a_r = b_r then A_eq_B = "Y";
run;
proc print data = s3;
var index a b A_eq_B a_r b_r;
format a hex16. b hex16. a_r hex16. b_r hex16.;
run;

(2)创建数值变量的字符串版本
data s3;
merge s1 s2;
by index;
a_fmt = put(a, 3.1);
b_fmt = put(b, 3.1);
if a_fmt = b_fmt then A_eq_B = "Y";
run;
proc print data = s3;
var index a b A_eq_B a_fmt b_fmt;
format a hex16. b hex16. a_fmt $hex16. b_fmt $hex16.;
run;

使用过程步中的选项
proc compare base = s1 compare = s2(rename = (b = a)) criterion = 0.1 method = absolute;
run;

参考文献:https://www.jianguoyun.com/p/DfN3qOQQ6YbXCRimmbME
SAS 数值存储方式和精度问题的更多相关文章
- 从java toBinaryString() 看计算机数值存储方式(原码、反码、补码)
一.toBinaryString 方法及其含义 1.1 方法说明 该方法位于java.lang.Integer类中 方法签名:public static String toBinaryString(i ...
- JS007. 深入探讨带浮点数运算丢失精度问题(二进制的浮点数存储方式)
复现与概述 当JS在进行浮点数运算时可能产生丢失精度的情况: 从肉眼可见的程度上观察,发生精度丢失的浮点数是没有规律的,但该浮点数丢失精度的问题会100%复现.经查阅,这个问题要追溯至浮点数的二进制存 ...
- float浮点数的二进制存储方式及转换
int和float都是4字节32位表示形式.为什么float的范围大于int? float精度为6-7位.1.66*10^10的数字结果并不是166 0000 0000 指数越大,误差越大. 这些问题 ...
- python 数据处理中各种存储方式里数据类型的转换
自己记录,仅供参考 在数据处理时经常会遇到数据类型不匹配的事情,为了方便查看各种存储方式中数据类型的改变.我把一些自己常用的整理方式记录下来,希望可以为以后数据类型的处理工作提供便利. 数据常用的基本 ...
- c语言中float、double、long double在内存中存储方式
存储格式中的二机制转为浮点数: 浮点型变量在计算机内存中占用4个字节(4 Byte),即32-bit,一个浮点数由2部分组成:底数m 和 指数e: 底数部分:使用2进制数来表示此浮点数的实际值: 指 ...
- C语言 float、double数据在内存中的存储方式
float在内存中占4个字节(32bit),32bit=符号位(1bit)+指数位(8bit)+底数位(23bit) 指数部分 指数位占8bit,可以表示数值的范围是0-(表示0~255一共256个数 ...
- C语言浮点数存储方式
对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用 32bit,double数据占用 64bit.其实不论是float类型还是double类型,在计算 ...
- .NET C#教程初级篇 1-1 基本数据类型及其存储方式
.NET C# 教程初级篇 1-1 基本数据类型及其存储方式 全文目录 (博客园).NET Core Guide (Github).NET Core Guide 本节内容是对于C#基础类型的存储方式以 ...
- C/C++浮点数在内存中的存储方式
一.内存表示 任何数据在内存中都是以二进制的形式存储的,浮点数的表示是把一个数的有效数字和数的范围在计算机的一个存储单元中分别予以表示,数的小数点位置随比例因子的不同而在一定范围内自由浮动.如下图是3 ...
随机推荐
- UITableView的全部属性、方法以及代理方法执行顺序,看过之后肯定有收获---董鑫
UITableView-------表视图--继承UIScrollView并遵守NSCoding协议 属性 frame-------------设置控件的位置和大小 backgroundColor-- ...
- 实现“手机qq”侧滑菜单 -- 吴欧
基本数据采集 经过体验,手机QQ采用的应该是线性动画,即视图缩放比例等随手指在屏幕上滑动的距离以一次方程的形式变化. 提取基本数据,向右侧滑达到最大幅度时: 1. 右侧主视图左边界距离屏幕左边界的 ...
- Linux文件系统与日志分析的了解
Linux文件系统与日志分析 1.inode和block概述 2.模拟inode耗尽实验 3.ext类型文件恢复 4.xfs类型文件恢复 5.日志文件 6.日志分析 1.文件:文件是存储在硬盘上的,硬 ...
- Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.2:test (default-test) on project gulimall-common: There are test failures.
对Maven打包时碰见的问题: Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.2:test (d ...
- AppiumForWin安装
尝试安装Windows版本的Appium 参考:http://www.cnblogs.com/fnng/p/4540731.html 第一步:安装node https://nodejs.org/en/ ...
- 2022年了有哪些值得推荐的.NET ORM框架?
前言: 最近有很多同学问我.NET方面有哪些好用的ORM框架,我觉得这方面的介绍网上应该会介绍的比较全面文章,于是我想搜一篇全面的介绍文章发给他们结果我发现网上说来说去基本上就是那几个,于是就有了这篇 ...
- “百度杯”CTF比赛 九月场Upload
首先生成一个php文件以下源代码: <script language="PHP">$fh=fopen("../flag.".strtolower(& ...
- python数据结构:数组和列表
线性结构有两种:数组和列表 array和list 其中list各项操作的时间复杂度如下 因为insert是在头部插入 所以列表所有元素后移,时间复杂度为O(n) remove移除列表中某个值的第一个匹 ...
- vim中的incsearch不好用,没有动态效果,为什么——incsearch is not working
虽然使用Linux好多年了,使用vim也好多年了,但是使用vim进行search的时候重来也没有想过要添加动态效果,近来突然有了兴致想要添加这个功能,不过发现居然不好用,在百度上找了好长时间居然没有一 ...
- 【C# 程序集】.net core 3.0中动态卸载|卸载程序集
原文:https://docs.microsoft.com/zh-cn/dotnet/core/dependency-loading/understanding-assemblyloadcontext ...