C# 十进制,如何添加尾随零

本文关键字:添加 十进制 何添加 | 更新日期: 2023-09-27 18:32:52

我必须在十进制值中添加尾随零。不仅用于显示(因此Format不是一个选项(,而且用于实际的基础数据,因为小数精度在我们的应用程序中很重要。

我试过了:

decimal value = 1M
decimal withPrecision = value + 0.000M;

这在许多情况下效果很好...奇怪的是不是全部。我调试了withPrecision中的值仍然是1M的情况,因为在运行时没有看到值的任何差异,而在即时窗口中没有看到相同的硬编码值。我也用了十进制。获取比特以查找差异 - 没有。

我试过(按照此处的建议调整十进制精度,.net(:

decimal value = 1M
decimal withPrecision = value * 1.000M;

效果很好 - 除了大小写值为零。然后结果是 0M,没有任何尾随零。我也不相信该解决方案,它在其他情况下也可能不起作用。

目前我与:

decimal value = 1M
decimal withPrecision = (value * 1.000M) + 0.000M;

这在我目前发现的所有情况下都有效...但看起来也不是很值得信赖。我还可以实现零的例外情况。

我认为FormatParse会起作用。我不太喜欢它。它看起来不是很快,我不明白为什么我必须将小数放入字符串中才能操作它。

我开始相信,对于如此简单的任务,没有干净的解决方案。

C# 十进制,如何添加尾随零

A decimal占用 128 位(16 个字节(,其中 1 位用于符号,96 位(12 字节(用于实际值,5 位用于存储小数点的位置。

当C#编译器看到1M时,它将其解析为{sign: 0, value: 1, point: 0},而1.0M则解析为{sign: 0, value: 10, point: 1}。但是,两者都表示相同的值(1M == 1.0M返回 true(,并且另一个解析器可以很容易地将1M1.0M映射到{sign: 0, value: 1, point: 0}

1M0.1M相加时会发生什么情况? 1M{sign: 0, value: 1, point: 0}0.1M{sign: 0, value: 1, point: 1},所以我们有两个不同精度的数字。然而,这没有问题:我们可以通过将点1加并将其值乘以 10{sign: 0, value: 10, point: 1} 来移动1M点。现在两个数字具有相同的点位置,我们可以通过简单地将它们的值相加来将它们相加,从而得到 {sign: 0, value: 11, point: 1} ,对应于 1.1M

因此,decimal的内部表示不会影响其运算的精度 - 每当需要时,都会移动小数点位置(并调整值(。

但是,如果出于某种原因,您的小数绝对必须具有一定的点位置(从您到目前为止发布的内容来看,我认为没有令人信服的理由 - 格式化纯粹是一个显示问题(,那么最简单的方法是使用 decimal(int, int, int, bool, byte) 构造函数(或交替decimal(int[])(。这允许您传入值(作为 3 个整数(、符号(作为布尔值(和点位置(作为字节(。如果您传递高于 0 的点位置,则必须自己乘以该值:1.000M必须构造为 new decimal(1000, 0, 0, false, 3) ,而不是new decimal(1, 0, 0, false, 3)(因为这会给你0.001M(。

*点位置限制为 [0-28],因此decimal不能表示点后面超过 28 位的数字。此外,该值必须在点前面和点后面的数字之间"拆分",因此非常大的数字会限制可用的精度,可能会将其削减以表示点前面的数字。

可能不是你所希望的答案,但看起来你将不得不使用 ToString(( 格式。 我建议您阅读此 MSDN 链接中的"备注"部分。

《备注》最后一段说:

比例因子还会保留十进制数中的任何尾随零。尾随零不会影响算术或比较运算中十进制数的值。但是,如果应用了适当的格式字符串,则 ToString 方法可能会显示尾随零。

正如我从您的评论中了解到的那样,您希望通过存储十进制值来避免用于存储精度的附加字段。别这样。它滥用了框架,即使您成功实现了这一点,它也可以在另一个框架版本/mono/等中停止工作。这种编程使您的代码库不可读且难以调试。

只需使用您自己的类型:

struct DecimalEx
{
   public decimal Value;
   public byte Precision;
}

一个简单的数据类型中容纳几个值既酷又有趣,但是如果您与他人共享代码,请尝试避免这种情况,否则您将很容易在地狱中获得特殊地位。

Decimal中,将0.04m添加到0.06m产生0.10m而不是0.1m的原因并不是尾随零有意义,而是基于以下事实的优化:

    在小数点后
  1. 添加具有相同位数的数字很快,但在小数点后调整位数会很慢。

  2. 一个值是小数点后
  3. 有一定位数的两个数字之和,可能会被添加到小数点后具有相同位数的更多数字中。

  4. 由于 #2,在算术运算后删除额外零所花费的努力更有可能增加后续操作所需的工作量,而不是减少工作量。

  5. 即使 0.10m 和 0.100m 之间实际上没有任何语义差异,让多个位模式表示相同的数字而没有任何方法来区分它们可能会导致一些烦恼和混乱,特别是如果有必要解决实现中的错误。

鉴于上述情况,我认为 Decimal 的ToString()尾随零行为更像是一种调试辅助工具,而不是一个可用的功能。