本文链接: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 数值存储方式和精度问题的更多相关文章

  1. 从java toBinaryString() 看计算机数值存储方式(原码、反码、补码)

    一.toBinaryString 方法及其含义 1.1 方法说明 该方法位于java.lang.Integer类中 方法签名:public static String toBinaryString(i ...

  2. JS007. 深入探讨带浮点数运算丢失精度问题(二进制的浮点数存储方式)

    复现与概述 当JS在进行浮点数运算时可能产生丢失精度的情况: 从肉眼可见的程度上观察,发生精度丢失的浮点数是没有规律的,但该浮点数丢失精度的问题会100%复现.经查阅,这个问题要追溯至浮点数的二进制存 ...

  3. float浮点数的二进制存储方式及转换

    int和float都是4字节32位表示形式.为什么float的范围大于int? float精度为6-7位.1.66*10^10的数字结果并不是166 0000 0000 指数越大,误差越大. 这些问题 ...

  4. python 数据处理中各种存储方式里数据类型的转换

    自己记录,仅供参考 在数据处理时经常会遇到数据类型不匹配的事情,为了方便查看各种存储方式中数据类型的改变.我把一些自己常用的整理方式记录下来,希望可以为以后数据类型的处理工作提供便利. 数据常用的基本 ...

  5. c语言中float、double、long double在内存中存储方式

    存储格式中的二机制转为浮点数: 浮点型变量在计算机内存中占用4个字节(4 Byte),即32-bit,一个浮点数由2部分组成:底数m  和 指数e: 底数部分:使用2进制数来表示此浮点数的实际值: 指 ...

  6. C语言 float、double数据在内存中的存储方式

    float在内存中占4个字节(32bit),32bit=符号位(1bit)+指数位(8bit)+底数位(23bit) 指数部分 指数位占8bit,可以表示数值的范围是0-(表示0~255一共256个数 ...

  7. C语言浮点数存储方式

    对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用 32bit,double数据占用 64bit.其实不论是float类型还是double类型,在计算 ...

  8. .NET C#教程初级篇 1-1 基本数据类型及其存储方式

    .NET C# 教程初级篇 1-1 基本数据类型及其存储方式 全文目录 (博客园).NET Core Guide (Github).NET Core Guide 本节内容是对于C#基础类型的存储方式以 ...

  9. C/C++浮点数在内存中的存储方式

    一.内存表示 任何数据在内存中都是以二进制的形式存储的,浮点数的表示是把一个数的有效数字和数的范围在计算机的一个存储单元中分别予以表示,数的小数点位置随比例因子的不同而在一定范围内自由浮动.如下图是3 ...

随机推荐

  1. TCP连接的状态转换图深度剖析

    转载请注明来源:https://www.cnblogs.com/hookjc/ 在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接,如图1所示. (1)第一次握手:建立连接时 ...

  2. mysql查询奇数行或者偶数行数据

    select * from (select @rownum := @rownum+1 as row_num, t.* from 表名 t,(select @rownum:=0) tmp_table o ...

  3. Lvs+Keepalived+MySQL Cluster架设高可用负载均衡Mysql集群

    ------------------------------------- 一.前言 二.MySQL Cluster基本概念 三.环境 四.配置 1.LB-Master及LB-Backup配置 2.M ...

  4. html页面动效

    找到了一个喜欢的黑客帝国动效"https://files.cnblogs.com/files/blogs/718959/codeMatrix-master.zip?t=1643081202& ...

  5. 简述redis集群的实现原理

    在哨兵sentinel机制中,可以解决redis高可用问题,即当master故障后可以自动将slave提升为master,从而可以保证redis服务的正常使用,但是无法解决redis单机写入的瓶颈问题 ...

  6. centos安装MySQL问题

    使用sudo yum install mysql-server出现没有可用软件包 mysql-server. 先 执行 wget http://repo.mysql.com/mysql-communi ...

  7. Linux 基础练习题

    Linux 测试 1.找出/proc/meminfo文件中以s开头的行,至少用三种方式忽略大小写 [root@localhost proc]# grep -i '^s' /proc/meminfo [ ...

  8. Solution -「HAOI 2018」「洛谷 P4491」染色

    \(\mathcal{Description}\)   Link.   用 \(m\) 种颜色为长为 \(n\) 的序列染色,每个位置一种颜色.对于一种染色方案,其价值为 \(w(\text{出现恰 ...

  9. C# 不区分大小写替换文本

    C# .NET类库自带的str.Replace() 方法替换文本不能区分大小写.我们可以自己编写一个扩展方法,支持文本忽略大小写替换.以下扩展方法实现了使用正则表达式忽略大小写替换文本. public ...

  10. 2022年了有哪些值得推荐的.NET ORM框架?

    前言: 最近有很多同学问我.NET方面有哪些好用的ORM框架,我觉得这方面的介绍网上应该会介绍的比较全面文章,于是我想搜一篇全面的介绍文章发给他们结果我发现网上说来说去基本上就是那几个,于是就有了这篇 ...