【问题标题】:SAS wrong sum after summary汇总后的 SAS 错误总和
【发布时间】:2015-11-11 10:33:22
【问题描述】:

我有一个包含 575965 行的表格。 “Ergebnisdaten”列的格式为 20.2

如果我提交以下内容:

 proc sql noprint;
  create table test as 
  select 
    Ergebnisdaten, 
    Ergebnisdaten*100 as euro format 20.4, 
    Ergebnisdaten*10000 as erg format 32.4,
    floor(Ergebnisdaten*10000) as floor format 20.4,
  floor(Ergebnisdaten*100)/100 as floor2 format 20.4
  from &source_lib..&source_table.;
quit;

proc sql noprint;
  select 
    sum(Ergebnisdaten) format=32.4, 
    sum(euro) format=32.4, 
    sum(erg) format=32.4, 
    sum(floor) format=32.4,
    sum(floor2) format=32.4
    into :sum_ges, :sum_euro, :sum_erg, :sum_floor, :sum_floor2
    from test;
 quit;

 %put Summe: &sum_ges.;  
 %put Summe: &sum_euro.;  
 %put Summe: &sum_erg.;  
 %put Summe: &sum_floor.;  
 %put Summe: &sum_floor2.;  

我得到 5 个不同的值:

380   %put Summe: &sum_ges.;

Summe:                 24507249859.0368

381   %put Summe: &sum_euro.;

Summe:               2450724985904.0000

382   %put Summe: &sum_erg.;

Summe:             245072498590400.0000

383   %put Summe: &sum_floor.;

Summe:             245072498562056.0000

384   %put Summe: &sum_floor2.;

Summe:                 24507249656.2654
  1. 我怎样才能对表格求和并以 20.4 格式获得正确的值 (24507249859.04)?

  2. 如果我在 zOS 上启动相同的程序,我会得到 24507249858.98。如何获得与在 Win/Unix 上计算的相同的值?

【问题讨论】:

  • 您是否在所有 3 个环境中使用相同的源数据集?如果不是,求和变量是否全部设置为数字长度 8?
  • 请注意,IBM 大型机使用与 Windows/Unix 机器不同的浮点表示。因此,您会看到无法精确表示为二进制数的小数差异。还有你是怎么转号的?显示的内容与实际存储的内容不匹配。因此,如果您使用 20.2 格式打印数字,然后以不同的值读回它们。
  • 你试过四舍五入吗? sum(round(Ergebnisdaten,0.01))

标签: sql sas zos


【解决方案1】:

如果没有看到实际数据,可能无法确定,但我的感觉是您遇到了浮点数的数值精度问题。

由于数字在计算机上以二进制形式存储,任何不能完全用二进制表示的东西都可能导致微小的差异。由于以 10 为底(十进制)有 2 和 5 作为因数,而二进制只有 2,因此您可以看到无法完美表示事物的地方。

例如,十进制小数 1/3 不能完美表示:

0.33333333333333

这尤其是一个将数字相加的问题,因为您要求 SAS 在上面做。例如:

1/3 + 1/3 + 1/3 = 1

但是

.33333333333333 + .33333333333333 + .33333333333333 = .99999999999999

假设您的存储空间有限,计算机会这样做。

通常,这不是问题。计算机存储结果的存储空间也有限,这往往意味着您在大多数情况下都能得到正确的答案。但是 - 并非所有时间。

由于您正在逐步达到双字节(双)浮点数中可能的最大精度,这使情况更加复杂。正如here 所讨论的,您可以看到 IEEE 系统(Unix、Windows)上的最大浮点数将是 52 位 - 大约 4*10^15 - 总共 16 位。你已经接近了,这意味着计算机用来假装整数实际上是整数的通常的舍入/模糊处理(通常不是)不会像你想要的那样工作,因为您几乎使用了整个两个字节。

这也是您可能遇到格式问题的原因。您会看到一点点浮点不准确——因为您需要所有这些数字。通常使用 BEST12。或BEST8。将隐藏所有这些混乱,但 32.4 或 20.2 显示完整的浮点数(任何超过 12 的都可能有一些问题,真的)。

至于如何处理它 - 好吧,你可能无法做到。舍入应该在视觉上修复它,只要舍入的数字可以或多或少地完全存储。如果有帮助,您的实际数字可以。使用 round(x,.01) 将其变为您认为应该的样子 - 但请理解这可能是不正确的。


当然,您在这里真正要问的问题是,为什么 *100 显示 04.00 而不是 *100 显示 .0368?

看这个:

data hex;
  exactN = 24507249859.04;
  almost = 24507249859.0368;
  integr = 2450724985904;
  put exactN= 32.4;
  put almost= 32.4;
  put integr= 32.4;
  put exactN= hex16.;
  put almost= hex16.;
  put integr= hex16.;

run;

日志如下。前 3 个是正常显示的数字,后 3 个是它们在计算机中的存储方式(以十六进制而不是二进制显示)。

exactN=24507249859.0400
almost=24507249859.0368
integr=2450724985904.0000
exactN=4216D2FBD30C28F6
almost=4216D2FBD30C25AF
integr=4281D4D4BCE18000

请注意,exactN 和几乎在十六进制表示中很接近 - 正如您所期望的那样,只有最后三位数字关闭了,因为差异接近精度的边缘(当然这是小端序)。但是 *100 完全不同。那是因为这是二进制的,所以你乘以 100 对计算机来说并不是很有趣:存储完全不同,因为这都是 2 的幂。如果你乘以 128,你会有一个非常相似的十六进制字符串(但左侧有所改变),但 100 最终得到一个完全不同的数字 - 这意味着这些小浮点不准确对于这个值是完全不同的,你最终得到 0400 而不是 0368结束。

【讨论】:

    【解决方案2】:

    通过要求格式 20.4,您是在告诉 SAS 您需要小数点后 4 位精度。如果您想要小数点后的 .04 并使用 20.4 格式,则需要对其进行四舍五入。

    【讨论】:

    • 他在问为什么它显示 0.0368 但当乘以 100 时显示 04.00,我想。这不仅仅是因为格式。
    【解决方案3】:

    你不能说:

    Ergebnisdaten*10000 格式 20.0 进入测试 然后重复整个过程,在 test1 表中再次使用格式 20.4 以 10000 决定。

    所以先把它放到一个丢失数字的表中,然后再放到下一个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多