C# 从单个到十进制的显式转换会导致不同的位表示

C# 从单个到十进制的显式转换会导致不同的位表示,c#,types,floating-point,decimal,primitive-types,C#,Types,Floating Point,Decimal,Primitive Types,如果我将singles转换为decimald,我注意到它的位表示与直接创建的十进制不同 例如: Single s = 0.01f; Decimal d = 0.01m; int[] bitsSingle = Decimal.GetBits((decimal)s) int[] bitsDecimal = Decimal.GetBits(d) 返回(为简洁起见删除了中间元素): 这两个数字都是十进制数字,它们都(似乎)准确地表示0.01: 看看说明书,除了以下几点之外,没有任何线索: §4.1

如果我将singles转换为decimald,我注意到它的位表示与直接创建的十进制不同

例如:

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

int[] bitsSingle = Decimal.GetBits((decimal)s)
int[] bitsDecimal = Decimal.GetBits(d)
返回(为简洁起见删除了中间元素):

这两个数字都是十进制数字,它们都(似乎)准确地表示0.01:

看看说明书,除了以下几点之外,没有任何线索:

§4.1.7与浮点和双精度数据类型相反,十进制小数 像0.1这样的数字可以精确地用十进制表示 代表性

这表明这在某种程度上受到单个无法准确表示转换前的0.01的影响,因此:

  • 为什么转换完成时这不准确
  • 为什么在同一数据类型中似乎有两种表示0.01的方法
TL;博士 两个小数精确地表示0.1。只是,
decimal
格式允许多个按位不同的值表示完全相同的数字

解释 这并不是说单个的不能精确地表示0.1。根据
GetBits
的文档:

十进制数的二进制表示形式由1位组成 符号、96位整数和用于分割 整数,并指定其小数部分。 比例因子隐式地是数字10,提升为指数 从0到28

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

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

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

位0至15(低位字)未使用,必须为零

位16至23必须包含介于0和28之间的指数,即 表示10除以整数的幂

位24至30未使用,必须为零

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

请注意,位表示法区分负数和负数 正零。这些值在所有方面都被视为相等 行动

对于
bitsSingle
bitsDecimal
而言,示例中每个
decimal
的第四个整数是
0x00030000
。在二进制中,此映射到:

bitsSingle     00000000 00000011 00000000 00000000
               |\-----/ \------/ \---------------/
               |   |       |             |
        sign <-+ unused exponent       unused
               |   |       |             |
               |/-----\ /------\ /---------------\
bitsDecimal    00000000 00000010 00000000 00000000

NOTE: exponent represents multiplication by negative power of 10
bitsSingle 00000000 000000 11 00000000 00000000
|\-----/ \------/ \---------------/
|   |       |             |

符号我遇到了一些依赖于Decimal.GetBits()
结果的代码,但除此之外,我认为这很有趣。答案比我的好,因为它是正确的。可以通过测试
0.01m.Equals((十进制)0.01f))轻松断言
1/100
10/1000
0.01
1%
1*10^-2
10*10^-3
都是相同数字的表示。尽管0.01m和(十进制)0.01f在数学上是相等的,但对它们调用
ToString
会产生不同的结果。所以这两者并不完全相同。@MichaelLiu:你的评论到底指的是什么?我没有以任何方式批评你的回答。我只是注意到(在回答OP的第二个问题时)在将0.1表示为小数点的各种方式中实际上存在着有意义的差异。@MichaelLiu:不用担心,我只是无法确定你指的是什么。似乎
decimal.ToString
也使用指数来决定要显示的小数位数(非常符合逻辑),尽管您无法预测强制转换的结果,因此无法真正将其称为功能IMO。
bitsSingle     00000000 00000011 00000000 00000000
               |\-----/ \------/ \---------------/
               |   |       |             |
        sign <-+ unused exponent       unused
               |   |       |             |
               |/-----\ /------\ /---------------\
bitsDecimal    00000000 00000010 00000000 00000000

NOTE: exponent represents multiplication by negative power of 10