<span id="bed6f2c35b"></span><address id="bf13ae16e8"><style id="bg303c6f08"></style></address><button id="bld5953152"></button>
                        

          澳门威尼斯娯乐城vn5002,澳门威尼斯娱乐平台,威尼斯娱城网址

          首页 > 应用开发 > C/C++ > 十进制浮点数的表示方法

          十进制浮点数的表示方法

          C/C++ 作者:pacman2000 时间:2018-12-06 12:07:45 1 删除 编辑

          使用十进制浮点数,可以避免二进制浮点数与我们习惯的十进制数之间的表示误差.这个在金融领域是非常重要的.但是计算机基本都只能对二进制浮点数进行计算,也就是IEEE754格式表示的浮点数.很多程序都会自己模拟十进制浮点数的计算.为了统一,IEEE754做了扩展,包括了十进制的浮点数.

          IEEE 754-2008里面规定了十进制浮点数的一些规范.不过里面没有说具体的二进制表示方法.只是规定了32位,64位,128位的十进制浮点数的表示范围和有效位数. 因为具体一个浮点数的二进制里面每个位表示啥,都是每个机器自己决定的.不需要跟外界一致.只是在传输的时候要保证数据的精度和范围一致就行了.下表来自wikipedia,列出了每种浮点数的有效位数,指数的范围.


          Name  Common name  Base  Digits  E min  E max 
          binary16  Half precision  2 10+1  -14 15
          binary32  Single precision  2 23+1  -126 127
          binary64  Double precision  2 52+1  -1022 1023
          binary128  Quadruple precision  2 112+1  -16382 16383
          decimal32 
          10 7 -95 96
          decimal64 
          10 16 -383 384
          decimal128 
          10 34 -6143

          6144 


          实际的系统中,十进制浮点数有两种表示方法,分别是Densely Packed Decimal(密集十进制数)和Binary Integer Decimal(二进制整数表示的十进制数).

          DPD表示方便转换成十进制的浮点数字符串,但是需要专门的计算单元来做计算,软件模拟比较麻烦.

          而BID表示更直观,转换到二进制会比较容易.很方便用二进制的整数运算单元来计算.


          所以Power6上有了硬件的十进制浮点计算单元,就用DPD表示.而在x86 x64 cpu上没有十进制计算单元, 各种软件实现的十进制浮点库默认大都用BID方式表示.比如Intel就实现了一个开源的c 语言的十进制浮点数库。http://software.intel.com/en-us/articles/intel-decimal-floating-point-math-library/


          十进制浮点的意义,在于更符合人们的习惯,比如下面的例子

          1. #include < stdio . h >

          2.   int main ( )

          3.   {

          4.      double a = 7 . ;

          5.      double b = 0 . 00007 ;

          6.      printf ( "%d/n" , a = = b * 100000 ) ;

          7.   }

           


          正确的输出应该是1,但是实际的输出结果是0,在做相等比较的时候,还不得不考虑一下这个误差了。而某些时候误差会在计算过程中累计,变成比较明显的错误了。

          如果用intel的十进制浮点库赖做这个计算,结果就会不同了。intel这个库明显还在试验阶段,用起来比较麻烦。

          1. int main ( )

          2.   {

          3.      Decimal64 a , b , c ;

          4.      _IDEC_round my_rnd_mode = _IDEC_dflround ;

          5.      _IDEC_flags my_fpsf = _IDEC_allflagsclear ;

          6.      a = bid64_from_int32 ( 7 ) ;

          7.     b = bid64_from_string ( "0.00007" , my_rnd_mode , & my_fpsf ) ;

          8.      c = bid64_mul ( b , bid64_from_int32 ( 100000 ) , my_rnd_mode , & my_fpsf ) ;

          9.     printf ( "%d/n" , bid64_quiet_equal ( a , c , & my_fpsf ) ) ;

          10.      return 0 ;

          11.   }

           

          使用和double位数相同的Decimal64,结果就是1了。这里显然不是精度的问题,而是十进制浮点数能丝毫不变的表示十进制的小数。

          我们可以看到这里使用的是BID的表示方法。函数名前面都带个bid前缀。

          接下来,我们来具体看看BID的表示方法,我们可以把刚才程序中的a和c按照十六进制输出

              printf("%llx/n%llx/n",a,c);

          结果是

          31c0000000000007
          31200000000aae60

          可见,两个相等的十进制浮点数的BID表示不一定是相同的。也就是说,一个数有多种表示方法。

          a的表示里,最低位的16进制数就是7,而c的表示里,最低的5位15进制数aae60,其实就是十进制的700000。看来这后面的就是有效数字部分了。查一下BID的表示方法,还是比较复杂的,有6种情况。最高位是符号位,这里当然是0.符号位后面的两位是00,01,或10时,64位BID每个位的意义是这样的,s后面的2位和之后的8位是指数部分,之后53位T和t都是有效数字部分

          s 00eeeeeeee TTTtttttttttttttttttttt tttttttttttttttttttttttttttttt
          s 01eeeeeeee TTTtttttttttttttttttttt tttttttttttttttttttttttttttttt
          s 10eeeeeeee TTTtttttttttttttttttttt tttttttttttttttttttttttttttttt
          而如果符号位后面的两位是11,那么每一位的意义是
          s 11 00eeeeeeee (100)Ttttttttttttttttttttt tttttttttttttttttttttttttttttt
          s 11 01eeeeeeee (100)Ttttttttttttttttttttt tttttttttttttttttttttttttttttt
          s 11 10eeeeeeee (100)Ttttttttttttttttttttt tttttttttttttttttttttttttttttt
          这时,有效数字前面就加了隐含的100.
          这个BID表示的数的值就是 (-1)^S *T*10^(E-398) ,其中T 是实际的有效数字(就是说如果有隐含的100需要加上后计算),E是指数,T,E都是2进制表示的
          还是回到我们的例子
          a的二进制数
          0 0110001110 00000 00000000 00000000 00000000 00000000 00000000 00000111
          指数部分就是0110001110,也就是398,所以a就是 7*10^(398-398) ,也就是7

          而c的二进制是
          0 0110001001 00000 00000000 00000000 00000000 00001010 10101110 01100000
          指数部分是 0110001001,也就是393, 所以c的值是 700000*10^(393-398), 还是7.
          这就能看明白为啥同样是7,二进制表示却不同。这也是十进制浮点和二进制浮点一个不同之处,十进制浮点没有规定一定要是哪一种表示。这也给相等比较带来了一点麻烦。


          power6 里面内置了十进制浮点计算单元,而power6上面的编译器也就支持了内置的十进制浮点类型。前面已经说了,power上面的十进制浮点才用的是DPD表示方法。还是看个程序吧。下面这个程序在一个使用Power6的P520机器上,操作系统是AIX5.3 ML6, 用xlc 10.2编译。_Decimal64就是64位的十进制浮点。

          1. int main ( int argc , char * * argv )

          2. {

          3. long i , count ;

          4. double dfund , dinterest ;

          5. _Decimal64 Dfund , Dinterest ; / * 定义十进制浮点类型的变量 * /

          6.  long long value ;

          7.  union trans {

          8.   _Decimal64 dv ;

          9.    int av [ 2 ] ;

          10.   } transTemp ;

          11.  dfund = atof ( argv [ 1 ] ) ;

          12.  dinterest = atof ( argv [ 2 ] ) ;

          13.  Dfund = atodecimal ( argv [ 1 ] ) ;

          14.  Dinterest = atodecimal ( argv [ 2 ] ) ;

          15.  count = atoi ( argv [ 3 ] ) ;

          16. / * 下面把_Decimal64 类型的Dinterest转换成两个int,然后按照十六进制格式显示 * /

          17.  transTemp . dv = Dinterest ;

          18.  printf ( "value=%#x,%#x/n" , transTemp . av [ ] , transTemp . av [ 1 ] ) ;

          19.  printf ( "double  fund=%20.10f interest=%40.30f/n" , dfund , dinterest ) ;

          20.  printf ( "Decimal fund=%20.10Df interest=%40.30Df/n" , Dfund , Dinterest ) ; / * printing them with the new printf specifiers * /

          21.   for ( i = ; i < count ; i + + ) {

          22.          dfund = dfund * dinterest ;

          23.          Dfund = Dfund * Dinterest ; / * performing maths * /

          24.   }

          25.  printf ( "Print final funds/n" ) ;

          26.  printf ( "double  fund=%30.10f/n" , dfund ) ;

          27.  printf ( "Decimal fund=%30.10Df/n" , Dfund ) ;

          28. }

           

          其中 atodecimal是自己写的一个帮助函数

          1. _Decimal64 atodecimal ( char * s )

          2. {

          3. _Decimal64 top = , bot = , result ;

          4. int negative = , i ;

          5.          if ( s [ ] = = ' - ' ) {

          6.                 negative = 1 ;

          7.                 s + + ;

          8.          }

          9.          if ( s [ ] = = ' + ' ) s + + ;

          10.           for ( ; isdigit ( * s ) ; s + + ) {

          11.                 top = top * 10 ;

          12.                 top = top + * s - ' ' ;

          13.          }

          14.          if ( * s = = ' . ' ) {

          15.                 s + + ;

          16.                  for ( i = strlen ( s ) - 1 ; isdigit ( s [ i ] ) ; i - - ) {

          17.                         bot = bot / 10 ;

          18.                         bot = bot + ( _Decimal64 ) ( s [ i ] - ' ' ) / ( _Decimal64 ) 10 ;

          19.                  }

          20.          }

          21.         result = top + bot ;

          22.          if ( negative )

          23.                 result = - result ;

          24.         return result ;

          25. }

           

          这个程序用xlc 10.2编译时,跟上参数表示使用硬件十进制浮点。不过这样会导致编译出来的可执行文件在power5以前的cpu上无法运行。

          运行的时候输入参数 ./dfp_hw 1 1.00000091 6000000

          dfp_hw是程序的名字,1就是程序里面的 fund,1.00000091是interest,也就是利息,6000000是count,输出结果:

          value=0x22180000,0x800001b
          double  fund=        1.0000000000 interest=        1.000000910000000020616539586630
          Decimal fund=        1.0000000000 interest=        1.000000910000000000000000000000
          Print final funds
          double  fund=                235.0968403429
          Decimal fund=                235.0968403137

          可以看到用double存储利息,再输出,就不再是1.00000091了,后面有一点误差。而用_Decimal64存储输入结果,再输出,是一点误差都没有。

          然后把interest乘6000000次,也就是1.0000091的6000000次方,输出的结果误差就比较明显了。用windows自带的计算器可以验证,_Decimal64的结果是正确的。

          现在来看看1.00000091的二进制表示。也就是0x22180000,0x800001b,注意这里这个power机器是大端的,所以前面以前是高4字节,后面是低4字节。连起来看,就是0x22180000 0800001b也就是

          00100010 00011000 00000000 00000000 00001000 00000000 00000000 00011011

          DPD表示方法也比较复杂,从高位开始看,第一位还是符号位0,DPD的规定如果符号位后面的两位是00,01,或者10,那么每一位的意义如下

          s 00 mmm (00)eeeeeeee (mmm)[tttttttttt][tttttttttt][tttttttttt][tttttttttt][tttttttttt]
          s 01 mmm (01)eeeeeeee (mmm)[tttttttttt][tttttttttt][tttttttttt][tttttttttt][tttttttttt]
          s 10 mmm (10)eeeeeeee (mmm)[tttttttttt][tttttttttt][tttttttttt][tttttttttt][tttttttttt]
          其中,e是指数,e的表示方法跟前面的BID方式很像。t和m是有效数字,其中,每10位t组成一个declet,表示一个3位的十进制数。m实际的位置是在第4位到第6位,但是它逻辑上的位置是在那些t前面,所以用()表示放到e的后面。

          因为2的10次方是1024,刚好能表示10的3次方。但是表示起来还是需要点技巧的,declet表示三位十进制数的规则比较复杂,这也是这个表示方法叫Densely Packed Decimal(密集十进制数)的原因。下表是编码的方式。b9-b0代表10个二进制数,d2 d1 d0代表3个十进制数。


          b9

          b8

          b7

          b6

          b5

          b4

          b3

          b2

          b1

          b0

          d2

          d1

          d0

          编码值

          数位的模式

          a

          b

          c

          d

          e

          f

          g

          h

          i

          0abc

          0def

          0ghi

          (0 7) (0 7) (0 7)

          3 位小数字

          a

          b

          c

          d

          e

          f

          1

          i

          0abc

          0def

          100i

          (0 7) (0 7) (8 9)

          两位小数字,一位大数字

          a

          b

          c

          d

          e

          f

          1

          1

          i

          0abc

          100f

          0dei

          (0 7) (8 9) (0 7)

          a

          b

          c

          d

          e

          f

          1

          1

          i

          100c

          0def

          0abi

          (8 9) (0 7) (0 7)

          a

          b

          c

          1

          f

          1

          1

          1

          i

          0abc

          100f

          100i

          (0 7) (8 9) (8 9)

          一位小数字,两位大数字

          a

          b

          c

          1

          f

          1

          1

          1

          i

          100c

          0abf

          100i

          (8 9) (0 7) (8 9)

          a

          b

          c

          f

          1

          1

          1

          i

          100c

          100f

          0abi

          (8 9) (8 9) (0 7)

          x

          x

          c

          1

          1

          f

          1

          1

          1

          i

          100c

          100f

          100i

          (8 9) (8 9) (8 9)

          三位大数字


          就我们的例子来看一下,最低的10位是0000011011,看b3b2b1,这里是101,所以就是上表第3行的情况,三位数字就是 (0000)(1001)(0001)也就是091,然后看从低位数的第3个10位二进制数,也就是00100000000,这显然是第一种情况,也就是100,连起来就是100000091,指数部分是390,那么这个十进制的值就是 10^(390-398)*100000091 = 1.00000091.

          通过这个简单的例子,就应该对DPD方式的十进制浮点表示方式有个大概的了解了。这个方式算起来比较麻烦,所以除非有硬件支持,软件模拟的方式都不会使用的,但是DPD转换成十进制浮点的字符串表示就会很方便。


           

          来自 “ 澳门威尼斯娯乐城vn5002,澳门威尼斯娱乐平台,威尼斯娱城网址 ” ,链接:/283313/viewspace-2284346/,如需转载,请注明出处,否则将追究法律责任。

          下一篇: 没有了~
          请登录后发表评论 登录
          全部评论

          注册时间:2010-03-16

          • 博文量
            43
          • 访问量
            48864