【问题标题】:C# Convert object to DecimalC# 将对象转换为十进制
【发布时间】:2011-10-14 00:24:41
【问题描述】:

我正在尝试将具有值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。

【问题讨论】:

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

标签: c# .net types precision


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    当您从数据库中读取数据时(正如您在其中一个 cmets 中指出的那样,IMO 您应该在您的问题中添加这一点)我认为在从数据库读取时允许转换为双精度和返回是一个糟糕的主意,因为您将失去精度[可能存储为定点或可以表示小数的数字系统]。 我认为您必须努力将存储的值直接读取为小数(编辑您的架构或类似的东西),或者如果不可能,则将它们作为字符串读取,并使用 Decimal.Parse() 获取实际值。

    实际上您的数字 0.39999999999999997 有 17 位小数,因此不能安全地存储为双精度数。

    附:有a great article 关于.net Doubles 和由 Jon Skeet 编写的四舍五入。

    【讨论】:

    • 我知道转换为双精度然后再返回是个坏主意。不幸的是,由于应用程序架构,这是我现在唯一的选择。展望未来,我想我将把值作为字符串读取,然后将其转换为十进制。感谢那篇文章。这是一本很棒的书。 :)
    【解决方案3】:

    我认为这是您要查找的代码:

    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);
    

    【讨论】:

    • 完美。虽然我担心使用双打会影响数据准确性,但这似乎是迄今为止最好的选择。谢谢。
    • 好吧,在这种情况下,我同意 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
    • 谢谢。很好的解释。 :)
    【解决方案4】:

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

    【讨论】:

    • 不是一个选项。实际值是从数据库中检索的。
    【解决方案5】:
    string val = "0.39999999999999997");
    decimal d = decimal.Parse(val,
        System.Globalization.NumberStyles.AllowDecimalPoint);//0.39999999999999997
    

    【讨论】:

    • object d = 0.39999999999999997; double decim = double.Parse(d.ToString(), System.Globalization.NumberStyles.AllowDecimalPoint); //decim = 0.4
    • @Chathura:这是一个错字。我的意思是decima.Parse 我测试了它,结果符合预期。
    • 嘿,字符串是从哪里出现的?
    • 哎呀。似乎开枪太快了。 'd.ToString()' 把它变回 0.4
    • @Chathura:我现在对其进行了测试,但它并没有回到0.4。这是我的测试:` string val = "0.39999999999999997"; decimal d = decimal.Parse(val, System.Globalization.NumberStyles.AllowDecimalPoint);/*0.39999999999999997*/ string s = d.ToString();/*0.39999999999999997*/`
    【解决方案6】:

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

    object d = 0.39999999999999997M;
    

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

    只有这样才会起作用decimal dec = Convert.ToDecimal(d);

    【讨论】:

    • 由于这个变量的初始化方式(实际值存储在数据库中)我无法知道它什么时候会是十进制值。
    • 如果你无论如何要使用十进制,为什么不获取数据库值并将其转换为十进制,那有什么问题?
    • 让我澄清一下。这是一个接受对象的通用方法。而且我无法更改方法签名。
    • 恐怕那是不可能的,因为对象持有对该值的引用,在您的情况下,除非引用是十进制的,否则您实际上无法获得该精度,它永远无法拆箱为小数,因为它从未被装箱。
    【解决方案7】:

    在此页面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,您只会失去精度。

    【讨论】:

    • Decimal 类型是必需的,因为 Double 存储它的值的​​方式,它不存储我输入的确切值。例如 12.03 将存储为 12.029999999999999 等。
    【解决方案8】:

    Decimal d = new Decimal(d);

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

    此构造函数使用四舍五入将值四舍五入到 15 位有效数字。即使数字超过 15 位且低位为 0,也会执行此操作。

    【讨论】:

    • 不起作用。无法将对象传递给 Decimal() 构造函数。
    猜你喜欢
    • 1970-01-01
    • 2017-07-31
    • 2013-07-20
    • 2012-06-17
    • 2022-06-22
    • 2021-06-26
    • 2010-10-15
    • 2017-07-19
    • 2018-11-18
    相关资源
    最近更新 更多