问题 C#将对象转换为Decimal


我正在尝试使用值转换对象 0.39999999999999997 到十进制变量而不会丢失精度。

object d = 0.39999999999999997;

我尝试了以下方法。

decimal val1 = Convert.ToDecimal(d); // val1 = 0.4
object val2 = Convert.ChangeType(d, Type.GetType("System.Decimal")); // val2 = 0.4
decimal val3 = decimal.Parse(d.ToString()); // val3 = 0.4
decimal val4 = (Decimal) d; // val4 = 0.4

我知道这不是十进制数据类型无法存储此值的问题,如下所示。

decimal val5 = 0.39999999999999997m; // val5 = 0.39999999999999997;

如何在不丢失精度的情况下将此对象转换为十进制?

如果重要的话,我正在使用.NET Framework 3.5。


1705
2017-07-27 07:12


起源

不太确定你能保证装箱/拆箱和不同浮动类型转换之间的持久精度。 - Tigran


答案:


我想这是你要找的代码:

object d = 0.39999999999999997;
//Unbox value
double doubleVal = (double)d;

//Convert to string. R format specifier gives a string that can round-trip to an identical number.  
//Without R ToString() result would be doubleAsString = "0.4"
string doubleAsString = doubleVal.ToString("R"); 

//Now that you have doubleAsString = "0.39999999999999997" parse it!
decimal decimalVal = decimal.Parse(doubleAsString);

8
2017-07-27 07:48



完善。虽然我担心使用双打会影响数据的准确性,但这似乎是迄今为止最好的选择。谢谢。 - Chathura W
那么在这种情况下我同意V4Vendettas的评论: I am afraid then it won't be possible since object holds a reference to the value and in your case unless the reference is of decimal you can't actually get that precision - Reniuz
谢谢。很好的解释。 :) - Chathura W


为此,您需要以类似方式进行分配

object d = 0.39999999999999997M;

除非你强制它,否则对象无法保持精度。 (如果这不是实际代码,则需要显示其分配方式)

只有这样,这样的事情才有用 decimal dec = Convert.ToDecimal(d);


4
2017-07-27 07:23



由于此变量的初始化方式(实际值存储在数据库中),我无法知道它何时是小数值。 - Chathura W
如果你无论如何要使用十进制为什么不获取数据库值并将其转换为十进制,那么问题是什么? - V4Vendetta
让我澄清一下。这是一个接受对象的通用方法。我无法改变方法签名。 - Chathura W
我担心然后它是不可能的,因为对象持有对值的引用,在你的情况下,除非引用是十进制的,你实际上不能得到那个精度,它永远不能解包到小数,因为它是从来没有盒装。 - V4Vendetta


当您从数据库中读取数据时(正如您在其中一条评论中所述,IMO您应该在问题中添加)我认为在从数据库读取时允许转换为double和back是一个糟糕的主意,因为您将失去精度[可能它存储为固定点或可以表示小数的数字系统]。 我认为你必须付出一些努力直接以小数形式读取存储的值(编辑你的模式或类似的东西),或者如果它不可能,那么将它们作为字符串读取,并使用Decimal.Parse()来获取实际值。

实际上你的号码0.39999999999999997有17位小数,因此不能安全地存储为double。

附:有 一篇好文章 关于.net由Jon Skeet写的双打和舍入。


1
2017-07-27 08:58



我知道转换成双倍然后再转回是一个坏主意。不幸的是,由于应用程序架构,这是我现在唯一的选择。继续前进,我想我将继续将值读取为字符串,然后将其转换为Decimal。谢谢那篇文章。这是一个很好的阅读。 :) - Chathura W


Decimal d = new Decimal(d);

如果d是double,那么根据文档 MSDN 这应该保持精度。

此构造函数使用舍入到最接近的值将值舍入为15位有效数字。即使数字超过15位且较低有效数字为零,也会执行此操作。


0
2017-07-27 07:17



不起作用。无法将对象传递给Decimal()构造函数。 - Chathura W
.Net 4.0和3.5都有一个带有double的构造函数... - Spence


在本页 http://msdn.microsoft.com/en-us/library/364x0z75(v=vs.80).aspx 它写成“没有后缀m,数字被视为双重”
而这段代码

object o = 0.39999999999999997;
Console.WriteLine(o.GetType());

出现 System.Double

而这一个

object o = 0.39999999999999997m;
Console.WriteLine(o.GetType());

出现 System.Decimal
所以你只是在没有后缀m的情况下失去你的精度。


0
2017-07-27 07:23



Decimal类型是必要的,因为Double存储它的值的​​方式,它不存储我输入的确切值。例如12.03将存储为12.029999999999999等。 - Chathura W


string val = "0.39999999999999997");
decimal d = decimal.Parse(val,
    System.Globalization.NumberStyles.AllowDecimalPoint);//0.39999999999999997

0
2017-07-27 07:16



object d = 0.39999999999999997; double decim = double.Parse(d.ToString(), System.Globalization.NumberStyles.AllowDecimalPoint); //decim = 0.4 - Chathura W
@Chathura:这是一个错字。我的意思是 decima.Parse 我按预期测试了它和结果。 - Jalal Said
嘿,字符串在哪里出现? - V4Vendetta
哎呦。看起来跳得太快了。 'd.ToString()'把它变回0.4 - Chathura W
@Chathura:我现在测试了它并没有回头 0.4。这是我的测试:`string val =“0.39999999999999997”; decimal d = decimal.Parse(val,System.Globalization.NumberStyles.AllowDecimalPoint); / * 0.39999999999999997 * / string s = d.ToString(); / * 0.39999999999999997 * /` - Jalal Said


对象d = 0.399999999999999999999999997M; 会帮你的。


0
2017-07-27 07:26



不是一种选择。从数据库中检索实际值。 - Chathura W


说真的,你要做的就是这样......

object d = 0.39999999999999997; 
decimal result;
decimal.TryParse(d.ToString(), out result);
return result;

0
2017-07-18 17:49



谢谢Sven的编辑 - Ben Brown