【问题标题】:Convert hexadecimal characters (more than 16 bytes) to decimal number in SAS在 SAS 中将十六进制字符(超过 16 个字节)转换为十进制数
【发布时间】:2018-11-05 05:25:55
【问题描述】:

您好,我有一个名为 Hexchar 的 SAS 字符列包含十六进制字符“0x1ba0c56c966ee8b0000”,这实际上是十进制数的 8154350000000000000000。我想将“0x1ba0c56c966ee8b0000”转换为 8154350000000000000000。如果十六进制小于 16 字节,那么下面的函数就可以了。

DexNumber=input(substr(Hexchar,3), hex16.); /* The reason why substr() function is to skip "0x" in the front */

但是十六进制格式仅限于 16 个字节,不能处理更多字节。你知道转换这个的任何好的逻辑吗?

【问题讨论】:

  • 尝试首先将十六进制数字转换为它们所代表的实际字节。 binary=input(string,$hex.); 然后将二进制字节转换为数字。我尝试使用number=input(binary,pib8.),但它没有给出你想要的值。解释如何将你拥有的字符串转换成你想要的数字。
  • SAS 对数字使用 8 字节 IEEE 浮点数。您不能存储超过大约 15 个十进制数字的精度。您想对这些十进制数超过 16 位的值做什么?
  • 您好,谢谢。 IEEE754 我知道。这是来自以太坊区块链信息。由于他们可以处理的最小单位是10**(-18)ETH,(它被称为WEI,类似于比特币中的SATOSHI)这个数字往往非常大。区块链本身不需要转换为十进制,但分析需要使用小数和相应的算术运算。

标签: sas hex decimal


【解决方案1】:

您将无法将如此大的整数精确地存储在 SAS 数字(64 位双精度数)中。

来自https://v8doc.sas.com/sashtml/win/numvar.htm

Windows 下 SAS 变量的有效数字和最大长度整数

| Length  | Largest Integer       | Exponential
| (Bytes) | Represented Exactly   | Notation 
+---------+-----------------------+------------
|    3    |                 8,192 | 2 ^ 13
|    4    |             2,097,152 | 2 ^ 21
|    5    |           536,870,912 | 2 ^ 29
|    6    |       137,438,953,472 | 2 ^ 37
|    7    |    35,184,372,088,832 | 2 ^ 45
|    8    | 9,007,199,254,740,992 | 2 ^ 53

因此,虽然C 中的所有 16 位十六进制值都可以清楚地适合 64 位整数,但并非所有这些都可以清楚地适合 SAS 数字(那些 >2**53)。然而,64 位双精度是 IEEE 标准,可以处理高达 ~ 10**51 的数字,尽管会损失一些精度。

这段代码显示了两个不同的大整数赋值被存储为与赋值不同的相同 SAS 数值。

data _null_;
  x = 837886600861902976;
  y = 837886600861902977;
  put x= 20. / y= 20.;
  if x=y then put 'Huh? Ooohhh.';
run;
---------------------
x=837886600861902848
y=837886600861902848
Huh? Ooohhh.

一种方法是将 c-hex 字符串拆分为“上”和“下”部分,并使用它们的输入执行算术运算。需要注意的是,当实际的十六进制值 > 2**53 时,会损失清晰度/精度。

data _null_;
  length H $30;

  input H;

  put h=;

  if H =: '0x' then do;
    HX = substr(H,3); * skip over 0x;

    if (length (HX) > 15) then do;
      HX_lower = substr(HX, length(HX)-14);
      HX_upper = substr(HX, 1, length(HX)-15);
      if length (HX_lower) > 15 then put 'ERROR: ' H= 'too big for two parts';

      H_lower = input (HX_lower,hex15.);
      H_upper = input (HX_upper,hex15.);

      U = H_upper * 16**15;
      L = H_lower;

      put / H;
      put HX_upper $16. ' ->' H_upper 20. ' ->' U 25.;
      put HX_lower $16. ' ->' H_lower 20. ' ->' L 25.;

      H_num = U + L;
    end;
    else do;
      H_num = input (HX, hex15.);
    end;
  end;
  else
    put 'ERROR: ' H= 'is not c hex';

  put H= H_num= comma30.;
datalines;
0x1
0x1b
0x1ba
0x1ba0
0x1ba0c
0x1ba0c5
0x1ba0c56
0x1ba0c56c
0x1ba0c56c9
0x1ba0c56c96
0x1ba0c56c966
0x1ba0c56c966e
0x1ba0c56c966ee
0x1ba0c56c966ee8
0x1ba0c56c966ee8b
0x1ba0c56c966ee8b0
0x1ba0c56c966ee8b00
0x1ba0c56c966ee8b000
0x1ba0c56c966ee8b0000
magic!
run;

【讨论】:

【解决方案2】:

通过了解这些精度问题,以下是我自己的转换逻辑。

data test;

长度 dum_s $24.; dum_s="1ba0c56c966ee8b0000";

do while(length(dum_s)<24);
    dum_s="0"||dum_s;
end;
    dum_dec1=input(substr(dum_s,1,8),hex8.);
    dum_dec2=input(substr(dum_s,9,8),hex8.);
    dum_dec3=input(substr(dum_s,17,8),hex8.);

    dum1=dum_dec1*16**16;
    dum2=dum_dec2*16**8;
    dum3=dum_dec3*16**0;
    dum=dum1+dum2+dum3;
    dum_ether=dum/10**18;

把 dum_ether;
运行;

以太坊交易哈希 0x57662d92cb24943079dec7d83d3c39fc7a1ae958b22de58ba62c8c4cb425cab3 它的值是六进制“0x1ba0c56c966ee8b0000”,我上面的逻辑给了我 8154.35 ethereum(10 ** 18 次 wei),这与站点相同。 https://etherscan.io/tx/0x57662d92cb24943079dec7d83d3c39fc7a1ae958b22de58ba62c8c4cb425cab3 当值变成浮点数时,我确实意识到了一些精度问题。

谢谢!

【讨论】:

  • 如果您需要深入研究,请考虑 Proc GROOVY 和 Java 的 BigDecimal
猜你喜欢
  • 1970-01-01
  • 2017-08-23
  • 2013-09-12
  • 1970-01-01
  • 1970-01-01
  • 2019-07-27
  • 2018-01-31
  • 2016-12-18
  • 1970-01-01
相关资源
最近更新 更多