从Single到Decimal的显式转换导致不同的位表示

时间:2014-03-09 09:12:33

标签: c# types floating-point decimal primitive-types

如果我将 s 转换为十进制 d 我发现它的位代码与直接创建小数。

例如:

Single s = 0.01f;
Decimal d = 0.01m;

int[] bitsSingle = Decimal.GetBits((decimal)s)
int[] bitsDecimal = Decimal.GetBits(d)

返回(为简洁起见,删除了中间元素):

bitsSingle:
[0] = 10
[3] = 196608

bitsDecimal:
[0] = 1
[3] = 131072

这两个都是十进制数,两者(看起来)都准确地代表0.01:

enter image description here

看看这个规范除了可能之外没有任何亮点:

  

§4.1.7与float和double数据类型相反,十进制小数   0.1之类的数字可以用十进制表示   表示。

建议在转换前无法准确表示0.01,因此:

  • 为什么转换完成时这不准确?
  • 为什么我们似乎有两种方法在同一数据类型中表示0.01?

1 个答案:

答案 0 :(得分:7)

TL; DR

两位小数精确表示0.1。只是decimal格式允许多个按位不同的值表示完全相同的数字。

解释

single无法准确表示0.1。根据{{​​1}}:

的文档
  

GetBits数字的二进制表示由1位组成   符号,96位整数,以及用于分割的缩放因子   整数,并指定它的小数部分。   缩放因子隐含地为数字10,提升为指数   范围从0到28。

     

返回值是一个由32位有符号整数组成的四元素数组。

     

返回数组的第一个,第二个和第三个元素包含   96位整数的低,中,高32位。

     

返回数组的第四个元素包含比例因子和   标志。它由以下部分组成:

     

0到15位,低位字未使用,必须为零。

     

第16位至第23位必须包含0到28之间的指数   表示10的幂除以整数。

     

第24至30位未使用且必须为零。

     

位31包含符号:0表示正数,1表示负数。

     

请注意,位表示区分负数和   正零。这些值被视为完全相同   操作

示例中每个Decimal的第四个整数为decimal 0x00030000bitsSingle 0x00020000。在二进制文件中,这映射到:

bitsDecimal

因此,在第一种情况下,96位整数除以10的附加因子,而第二位16到23则给出值3而不是2.但是它被96位整数偏移本身,在第一种情况下也是第二种情况的10倍(显而易见的是第一种元素的价值)。

因此,观察值的差异可以归结为这样一个事实:bitsSingle 00000000 00000011 00000000 00000000 |\-----/ \------/ \---------------/ | | | | sign <-+ unused exponent unused | | | | |/-----\ /------\ /---------------\ bitsDecimal 00000000 00000010 00000000 00000000 NOTE: exponent represents multiplication by negative power of 10 的转换使用了与“直”构造函数相比的微妙不同的逻辑来推导内部表示。