【问题标题】:C# decimal, how to add trailing zerosC#十进制,如何添加尾随零
【发布时间】:2015-01-31 21:23:24
【问题描述】:

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

我试过了:

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

这在很多情况下都很好用……奇怪的是,根本不是。我调试到 withPrecision 中的值仍然为 1M 的情况,因为在运行时看不到任何差异,而在即时窗口中看不到相同的硬编码值。我还使用了 decimal.GetBits 来查找差异 - 没有。

我试过了(这里建议Adjusting decimal precision, .net):

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

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

目前我在:

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

这适用于我目前发现的所有情况......但看起来也不太值得信赖。我还可以为零实现一个例外情况。

我认为Format 和Parse 会起作用。我不太喜欢。它看起来不是很快,我不明白为什么我必须将小数放入一个字符串中才能对其进行操作。

我开始相信这样一个简单的任务没有干净的解决方案。

【问题讨论】:

  • 它可能是重复的,但不幸的是那里的答案不起作用(正如我所说:乘法不适用于零)。所以把这个问题当作当建议的答案不起作用时该怎么办......
  • “增加精度”到底是什么意思?调整比例不会改变小数的逻辑值,应该不会影响任何操作,那还有什么意义(除了显示,纯属格式问题)?
  • 您的链接指向几个答案。重要的是第二个答案(Andrew Hare)。所谓的精度只是格式问题,与数据的真实精度无关。
  • @Pieter:我们有自己的基于小数的价值体系。有我们自己的操作,转换和比较。其中一部分已经由小数本身完成。这些值以“原始”小数和表示“精度”的整数形式出现,即该点之后的位数。它都可以存储在一个小数内,所以我尝试实际使用它。它被舍入或扩展到给定的精度。精度也用于进一步处理和显示。

标签: c# decimal precision digits


【解决方案1】:

可能不是您希望的答案,但看起来您必须使用 ToString() 格式化。我建议您阅读this MSDN link 中的备注部分。

备注中的最后一段陈述:

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

【讨论】:

    【解决方案2】:

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

    只需使用您自己的类型:

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

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

    【讨论】:

    • 我们已经有了这样的类型。当然,它要复杂得多。因此,使用此类并编写一个构造函数,以确保 Value 在点之后始终具有指定的位数。那么你最终会回答我的问题。
    【解决方案3】:

    一个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),另一个解析器可以轻松地将 1M 和 1.0M 映射到 {sign: 0, value: 1, point: 0}。

    将1M 和0.1M 加在一起会发生什么? 1M 是 {sign: 0, value: 1, point: 0} 和 0.1M 是 {sign: 0, value: 1, point: 1},所以我们有两个精度不同的数字。不过这没问题:我们可以通过将1 添加到它的点并将其值乘以10 来移动1M 中的点:{sign: 0, value: 10, point: 1}。现在两个数字的点位置相同,我们可以通过简单地将它们的值相加来将它们相加,得到{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 位的数字。此外,该值必须在点前面和点后面的数字之间“拆分”,因此非常大的数字将对可用精度施加限制,可能会减少它以表示点前面的数字。

    【讨论】:

      【解决方案4】:

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

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

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

      3. 由于#2,在算术运算后消除多余零所花费的精力更有可能增加后续运算所需的工作量,而不是减少它。

      4. 1234563有必要对实现中的错误进行故障排除。

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

      【讨论】:

        猜你喜欢
        • 2011-02-12
        • 1970-01-01
        • 2012-12-06
        • 2014-07-26
        • 2012-06-28
        • 2016-11-24
        • 2011-04-28
        • 2021-12-04
        • 1970-01-01
        相关资源
        最近更新 更多