浅谈 System.Decimal 结构

本文探讨了 .NET Framework 中 System.Decimal 结构的内部表示,包括它的范围、存储方式及其实现原理。通过引入 TinyDecimal 结构进行简化说明,解释了如何用 8 位来表示一个具有 1 位小数的十进制数。此外,文章还提供了一个测试程序,展示了在 Linux 和 Windows 上 decimal 运算的不同舍入规则。
摘要由CSDN通过智能技术生成

引言

我们知道,Microsoft .NET Framework 中的 System.Decimal 结构(在 C# 语言中等价于 decimal 关键字)用来表示十进制数,范围从 -(296 - 1) 到 296 - 1,并且可以有 28 位小数。这就是说:

  • decimal.MinValue = -79,228,162,514,264,337,593,543,950,335 = -(296 - 1)
  • decimal.MaxValue = 79,228,162,514,264,337,593,543,950,335 = 296 - 1
  • decimal.Epsilon = 0.0000000000000000000000000001 = 10-28

上面前两个是 decimal 的静态只读字段。遗憾的是,第三个不属于 decimal 结构。

decimal 内部使用 4 个 32-bit 的 System.Int32 来存储,占用 128 bits = 16 bytes。这 128 bits 分配如下:

  • 96 bits 表示从 0 至 296 - 1 的整数,分布在 3 个 32-bit 的 System.Int32 中。
  • 剩下的 1 个 32-bit 的 System.Int32 包括符号位和比例因子。
  • 第 31 bit 是符号位,0 表示正数,1 表示负数。
  • 第 16 至 23 bit 表示比例因子,必须包含一个 0 至 28 之间的指数,指示 10 的幂,即小数点的位置,也就是小数点右边有几位数字。
  • 其实表示 0 至 28 之间的指数只需 5 bits 就够了,而上面的第 16 至 23 bit 共 8 bits = 1 byte。也就是说剩下的 3 bits (第 21 至 23 bit) 一定是零。
  • 其余 bits (0 - 15 bit 和 24 - 30 bit)不被使用,必须为零。

decimal.GetBits 方法就返回上述 decimal 的内部表示。而 decimal (int[] bits) 构造函数就使用这个内部表示构造来构造 decimal 实例。一个 decimal 可能会有几种不同的内部表示,所有这些内部表示均同样有效,并且在数值上相等。

TinyDecimal 数据类型

为了更好地理解 decimal 结构,我们来构造一个只有 8 bits = 1 byte 的 TinyDecimal 结构:

  • number: 第 0 至 5 bit (共 6 bits)表示从 0 至 26 - 1 的整数,共有 64 个。
  • exp: 第 6 bit 表示比例因子,包含一个 0 至 1 之间的指数,指示 10 的幂,即小数点的位置。0 表示小数点在最右边。
  • sign: 第 7 bit 是符号位,0 表示正数,1 表示负数。

因此:

  • TinyDecimal.MinValue = -63 = -(26 - 1)
  • TinyDecimal.MaxValue = 63 = 26 - 1
  • TinyDecimal.Epsilon = 0.1 = 10-1

也就是说,TinyDecimal 的表示范围从 -63 至 63,并且可以有 1 位小数。

TinyDecimal 的正数有以下两种情形:

  • 当 exp = 1 时: 0.1, 0.2, ... , 0.9, 1.0, 1.1, ... , 6.2, 6.3 。共 63 个。
  • 当 exp = 0 时:1, 2, ... , 63
import decimal def calculate_pi(): decimal.getcontext().prec = 35 pi = decimal.Decimal() k = while True: term = decimal.Decimal((-1) ** k) * (decimal.Decimal(2) ** (decimal.Decimal(5) * decimal.Decimal(k))) / (decimal.Decimal(4 * k + 1) * decimal.Decimal(math.factorial(k)) ** 2 * decimal.Decimal(396 ** (4 * k))) pi += term if abs(term) < decimal.Decimal(1e-35): break k += 1 return pi * decimal.Decimal(2 ** 6) def calculate_tan(x): decimal.getcontext().prec = 35 tan = decimal.Decimal() k = while True: term = decimal.Decimal((-1) ** k) * decimal.Decimal(2 ** (2 * k + 1)) * decimal.Decimal((2 ** (2 * k + 1) - 1)) * decimal.Decimal(x ** (2 * k + 1)) / decimal.Decimal(math.factorial(2 * k + 1)) tan += term if abs(term) < decimal.Decimal(1e-35): break k += 1 return tan def calculate_pi_with_tan(): decimal.getcontext().prec = 35 pi = decimal.Decimal() k = while True: term = decimal.Decimal((-1) ** k) * (decimal.Decimal(2) ** (decimal.Decimal(5) * decimal.Decimal(k))) / (decimal.Decimal(4 * k + 1) * decimal.Decimal(math.factorial(k)) ** 2 * decimal.Decimal(396 ** (4 * k))) * calculate_tan(decimal.Decimal(1) / decimal.Decimal(239)) pi += term if abs(term) < decimal.Decimal(1e-35): break k += 1 return pi * decimal.Decimal(2 ** 6) def kahan_sum(numbers): decimal.getcontext().prec = 35 sum = decimal.Decimal() c = decimal.Decimal() for number in numbers: y = number - c t = sum + y c = (t - sum) - y sum = t return sum pi = calculate_pi_with_tan() pi = kahan_sum([pi] * 10) print(pi) 这段代码有一些缺漏,请补充以便它计算出pi的值
03-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值