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

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

如果我将 single s 转换为 decimal 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 Contrary to the float and double data types, decimal fractional numbers such as 0.1 can be represented exactly in the decimal representation.

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

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

最佳答案

长话短说

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

说明

这与 single 不能精确表示 0.1 无关。根据 GetBits 的文档:

The binary representation of a Decimal number consists of a 1-bit sign, a 96-bit integer number, and a scaling factor used to divide the integer number and specify what portion of it is a decimal fraction. The scaling factor is implicitly the number 10, raised to an exponent ranging from 0 to 28.

The return value is a four-element array of 32-bit signed integers.

The first, second, and third elements of the returned array contain the low, middle, and high 32 bits of the 96-bit integer number.

The fourth element of the returned array contains the scale factor and sign. It consists of the following parts:

Bits 0 to 15, the lower word, are unused and must be zero.

Bits 16 to 23 must contain an exponent between 0 and 28, which indicates the power of 10 to divide the integer number.

Bits 24 to 30 are unused and must be zero.

Bit 31 contains the sign: 0 mean positive, and 1 means negative.

Note that the bit representation differentiates between negative and positive zero. These values are treated as being equal in all operations.

您的示例中每个decimal 的第四个整数是0x00030000 for bitsSingle0x00020000 for 位十进制。在二进制中,这映射到:

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

NOTE: exponent represents multiplication by negative power of 10

因此,在第一种情况下,与第二种情况相比,96 位整数除以一个额外的因子 10——第 16 位到第 23 位给出值 3 而不是 2。但这被 96 位整数抵消了本身,在第一种情况下也比第二种情况大 10 倍(从第一个元素的值可以明显看出)。

观察值的差异因此可以简单地归因于这样一个事实,即与“直接”构造函数相比,从 single 的转换使用微妙不同的逻辑来导出内部表示。

关于c# - 从 Single 到 Decimal 的显式转换导致不同的位表示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22280392/

相关文章:

c# - 仅选择美国时区

java - 什么时候在 java 中使用 linkedhashmap 而不是 hashmap?

C 'generics' -- double 和 float

scala - 我们可以使用单例 .type 作为类型参数吗?

c - 读取 float

c# - .NET 4 中的自定义属性更改

c# - 使用三元运算符检查 null - null 引用是否与 null 不同

c# - 从具有整数属性的 List<T> 返回 List<int>?

c++ - 存储 float 的最佳方式

c - 在C中添加不同类型的变量