简体   繁体   English

C#decimal,如何添加尾随零

[英]C# decimal, how to add trailing zeros

I have to add trailing zeros to a decimal value. 我必须将尾随零添加到十进制值。 Not only for displaying (so Format is not an option), but in the actual underlying data, because the decimal precision is important in our application. 不仅用于显示(因此Format不是一个选项),而是在实际的底层数据中,因为小数精度在我们的应用程序中很重要。

I tried: 我试过了:

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

Which works well in many cases ... strangely not in all. 在许多情况下哪种方法效果很好......奇怪的是并非总体而言。 I debugged into a case where the value in withPrecision was still 1M, by not seeing any difference in the value at runtime and the same hardcoded value in the immediate window. 我调试了一个情况,其中withPrecision中的值仍然是1M,没有看到运行时的值和立即窗口中相同的硬编码值有任何差异。 I also used decimal.GetBits to find differences - there are none. 我还使用了decimal.GetBits来找到差异 - 没有。

I tried (as proposed here Adjusting decimal precision, .net ): 我试过(这里提出调整小数精度,.net ):

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

which works well - except for the case value is zero. 效果很好 - 除了案例值为零。 Then the result is 0M without any trailing zeros. 然后结果为0M,没有任何尾随零。 I also do not trust the solution, it may also not work in other cases. 我也不相信解决方案,在其他情况下也可能不起作用。

Currently I'm with: 目前我在:

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

Which works in all cases I currently found ... but doesn't look very trustworthy neither. 这在我目前发现的所有情况下都有效......但是看起来也不值得信赖。 I could also implement an exceptional case for zeros. 我还可以为零实现一个例外情况。

I think Format and Parse would work. 我认为FormatParse会起作用。 I don't like it much. 我不喜欢它。 It doesn't look very fast and I don't understand why I have to put the decimal into a string just to manipulate it. 它看起来不是很快,我不明白为什么我必须将小数字放入一个字符串只是为了操纵它。

I start to believe that there is no clean solution for such a simple task. 我开始相信这样一个简单的任务没有干净的解决方案。

A decimal occupies 128 bits (16 bytes), of which 1 bit is used for the sign, 96 bits (12 bytes) are used for the actual value and 5 bits are used to store the position of the decimal point. decimal占用128位(16字节),其中1位用于符号,96位(12字节)用于实际值,5位用于存储小数点的位置。

When the C# compiler sees 1M , it parses it as {sign: 0, value: 1, point: 0} , while 1.0M is parsed as {sign: 0, value: 10, point: 1} . 当C#编译器看到1M ,它将其解析为{sign: 0, value: 1, point: 0} ,而1.0M被解析为{sign: 0, value: 10, point: 1} However, both represent the same value ( 1M == 1.0M returns true), and another parser could easily have mapped both 1M and 1.0M to {sign: 0, value: 1, point: 0} . 但是,两者都表示相同的值( 1M == 1.0M返回true),另一个解析器可以轻松地将1M1.0M映射到{sign: 0, value: 1, point: 0}

What happens when you add 1M and 0.1M together? 1M0.1M在一起会发生什么? 1M is {sign: 0, value: 1, point: 0} and 0.1M is {sign: 0, value: 1, point: 1} , so we have two numbers with different precision. 1M{sign: 0, value: 1, point: 0}0.1M{sign: 0, value: 1, point: 1} ,所以我们有两个精度不同的数字。 That's no problem however: we can move the point in 1M by adding 1 to its point and by multiplying its value by 10 : {sign: 0, value: 10, point: 1} . 然而,这没有问题:我们可以通过在其点加1并将其值乘以10来移动1M点: {sign: 0, value: 10, point: 1} Now that both numbers have the same point position, we can add them together by simply adding up their values, which results in {sign: 0, value: 11, point: 1} , which corresponds to 1.1M . 现在两个数字具有相同的点位置,我们可以通过简单地将它们的值相加来将它们加在一起,这导致{sign: 0, value: 11, point: 1} ,这对应于1.1M

So the internal representation of a decimal does not affect the precision of its operations - the decimal point position is moved (and the value adjusted) whenever this becomes necessary.* 因此, decimal的内部表示不会影响其操作的精度 - 只要有必要,就会移动小数点位置(并调整值)。*

However, if for some reason your decimals absolutely must have a certain point position (and from what you've posted so far, I see no compelling reason - formatting is purely a display issue), then the easiest approach is to use the decimal(int, int, int, bool, byte) constructor (or alternately decimal(int[]) ). 但是,如果由于某种原因你的小数绝对必须有一个点位置(并且从你到目前为止发布的,我看到没有令人信服的理由 - 格式化纯粹是显示问题),那么最简单的方法是使用decimal(int, int, int, bool, byte)构造函数(或者decimal(int[]) )。 This allows you to pass in the value (as 3 integers), the sign (as a boolean) and the point position (as a byte). 这允许您传入值(作为3个整数),符号(作为布尔值)和点位置(作为字节)。 You will have to multiply the value yourself if you pass a point position higher than 0: 1.000M must be constructed as new decimal(1000, 0, 0, false, 3) , not as new decimal(1, 0, 0, false, 3) (because that would give you 0.001M ). 如果传递高于0的点位置, 1.000M必须自己乘以该值: 1.000M必须构造为new decimal(1000, 0, 0, false, 3) 1.000M new decimal(1000, 0, 0, false, 3) ,而不是new decimal(1, 0, 0, false, 3) 1.000M new decimal(1, 0, 0, false, 3) (因为那会给你0.001M )。

*the point position is limited to [0-28], so a decimal cannot represent numbers with more than 28 digits behind the dot. *点位置限制为[0-28],因此decimal不能表示点后面超过28位的数字。 Also, the value has to be 'split' between digits in front of the dot and behind the dot, so very large numbers will put restrictions on the available precision, possibly cutting it down in favor of representing the digits in front of the dot. 此外,该值必须在点前面和点后面的数字之间“分开”,因此非常大的数字会对可用精度施加限制,可能会将其缩小以支持在点前面表示数字。

Probably not the answer you were hoping for, but it looks like you will have to use formatting with ToString(). 可能不是你希望的答案,但看起来你必须使用ToString()的格式。 I recommend you read the Remarks section in this MSDN link . 我建议您阅读此MSDN链接中的“ 备注”部分。

The last paragraph in Remarks states: 备注中的最后一段说明

The scaling factor also preserves any trailing zeros in a Decimal number. 缩放因子还保留十进制数中的任何尾随零。 Trailing zeros do not affect the value of a Decimal number in arithmetic or comparison operations. 尾随零不会影响算术或比较运算中的十进制数的值。 However, trailing zeros might be revealed by the ToString method if an appropriate format string is applied. 但是,如果应用了适当的格式字符串,ToString方法可能会显示尾随零。

As I understand from your comment, you want to avoid additional field for storing precision by storing in decimal value. 正如我从您的评论中理解的那样,您希望通过存储十进制值来避免存储精度的附加字段。 Don't do this. 不要这样做。 It's abusing the framework and even if you successfully implement this, it can stop working in another framework version/mono/etc. 它滥用框架,即使你成功实现了它,它也可以停止在另一个框架版本/ mono / etc中工作。 This kind of programming makes your code base unreadable and hard to debug. 这种编程使您的代码库不可读且难以调试。

Just use your own type: 只需使用您自己的类型:

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

It's cool and fun to fit couple of values in one simple data type, but if your sharing code with others, try avoiding that, or you will easily earn your special place in hell for that. 在一种简单的数据类型中拟合几个值是很酷和有趣的,但如果你与他人共享代码,请尝试避免这种情况,否则你很容易在地狱中获得特殊的地位。

The reason that, in Decimal , adding 0.04m to 0.06m yields 0.10m rather than 0.1m is not that the trailing zero is meaningful, but instead an optimization based upon the facts that: 之所以,在Decimal ,加上0.04m0.06m产生0.10m而不是0.1m并不是尾随零是有意义的,而是基于以下事实的优化:

  1. Adding numbers with the same number of digits after the decimal point is fast, but adjusting the number of digits after the decimal point is slow. 在小数点后添加具有相同位数的数字很快,但调整小数点后的位数很慢。

  2. A value which is the sum of two numbers with some number of digits after the decimal point is likely to be added to more numbers with the same number of digits after the decimal point. 一个值是小数点后面带有一些位数的两个数字的总和,很可能会被添加到小数点后面的位数相同的更多数字。

  3. Because of #2, effort expended to remove extra zeroes following an arithmetic operation would more likely increase the effort required for a succeeding operation than reduce it. 由于#2,在算术运算之后消耗额外零的努力更有可能增加后续操作所需的工作量而不是减少它。

  4. Even if there isn't really supposed to be any semantic difference between 0.10m and 0.100m, having multiple bit patterns represent the same number without having any way to distinguish them could cause some annoyance and confusion, especially if it is ever necessary to troubleshoot bugs in the implementation. 即使不存在0.10m和0.100m之间的任何语义差异,但是多个位模式代表相同的数字而无法区分它们可能会引起一些烦恼和混乱,特别是如果有必要进行故障排除实施中的错误。

Given the above, I would view the ToString() trailing-zeroes behavior with Decimal as being more of a debugging aid than a usable feature. 鉴于上述情况,我将使用Decimal查看ToString() trailing-zeroes行为,因为它更像是一个调试辅助工具,而不是一个可用的功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM