【问题标题】:Adjusting decimal precision, .net调整小数精度,.net
【发布时间】:2010-11-11 02:11:49
【问题描述】:

C# 中的这些行

decimal a = 2m;
decimal b = 2.0m;
decimal c = 2.00000000m;
decimal d = 2.000000000000000000000000000m;

Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
Console.WriteLine(d);

生成此输出:

2
2.0
2.00000000
2.000000000000000000000000000

所以我可以看到,从文字创建一个十进制变量可以让我控制精度。

  • 能否在不使用文字的情况下调整十进制变量的精度?
  • 如何从 a 创建 b?如何从 c 创建 b?

【问题讨论】:

    标签: .net decimal precision


    【解决方案1】:

    您只是看到完全相同数据的不同表示。 decimal 的精度将被缩放到它需要的大小(在合理范围内)。

    来自System.Decimal

    十进制数是浮点数 由一个符号组成的值,a 数字值,其中每个数字 值范围从 0 到 9,a 比例因子,表示 浮点小数点的位置 将积分和 数值的小数部分。

    十进制的二进制表示 值由一个 1 位符号组成,一个 96 位整数和缩放 用于划分 96 位的因子 整数并指定它的哪一部分 是一个小数。缩放 factor 隐含地是数字 10, 提高到从 0 的指数 到 28. 因此,二进制 十进制值的表示是 形式为 ((-296 到 296) / 10(0 到 28)),其中 -296-1 等于 MinValue,并且 296-1 等于 最大值。

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

    【讨论】:

    • 关于比例因子的好信息......现在我该如何调整它?
    • 比例因子不能直接控制,因为它会根据指数的大小进行调整。这基本上就是浮点的工作方式(以及“浮点”一词的来源)。由于有效位的位数是恒定的,所以数字的大小决定了有效位的比例。
    • 它会自动调整以适应它所包含的数据的需要。您是否有特殊原因需要手动扩展它?
    • 这是有原因的——但是,我觉得这个原因并没有使这个问题更加清晰。我可能会单独问一个关于原因的问题。
    • @AndrewHare 我只需要快速确认 - 我相信精度(数字中的总位数)和比例(小数点后的总位数)的定义在 .net 和 SQL 世界中是相同的对于十进制数据类型。我认为这些精度和规模概念与数学世界有关,而不是与 .Net 或 SQL 有关。如果我错了,请纠正我。
    【解决方案2】:

    【讨论】:

    • 对于 .00 值,您必须先乘以 1.00m:decimal.Round(value*1.00m, 2)
    【解决方案3】:

    问题是——您真的需要存储小数的精度,而不是只显示所需精度的小数。 大多数应用程序在内部都知道它们想要达到的精度并显示到该精度级别。例如,即使用户在帐户包中输入了 100 的发票,它仍然使用 val.ToString("n2") 之类的东西打印为 100.00。

    如何从 a 创建 b?如何从 c 创建 b?

    c 到 b 是可能的。

    Console.WriteLine(Math.Round(2.00000000m, 1)) 
    

    产生 2.0

    a 到 b 很棘手,因为引入精度的概念对数学来说有点陌生。

    我想一个可怕的黑客可能是一个往返。

    decimal b = Decimal.Parse(a.ToString("#.0"));
    Console.WriteLine(b);
    

    产生 2.0

    【讨论】:

      【解决方案4】:

      我发现我可以通过乘以或除以一个花哨的 1 来“篡改”刻度。

      decimal a = 2m;
      decimal c = 2.00000000m;
      decimal PreciseOne = 1.000000000000000000000000000000m;
        //add maximum trailing zeros to a
      decimal x = a * PreciseOne;
        //remove all trailing zeros from c
      decimal y = c / PreciseOne;
      

      我可以制造一个足够精确的 1,以通过已知大小改变比例因子。

      decimal scaleFactorBase = 1.0m;
      decimal scaleFactor = 1m;
      int scaleFactorSize = 3;
      
      for (int i = 0; i < scaleFactorSize; i++)
      {
        scaleFactor *= scaleFactorBase;
      }
      
      decimal z = a * scaleFactor;
      

      【讨论】:

      • 很有趣,但同时也令人费解。 2.0m 和 2.00000000m 之间到底有什么区别?在非计算环境中,我认为这意味着后一个数字保证了该精度,而前者只保证了第一个小数点。但是乘法应该意味着结果只精确到小数点后一位。
      【解决方案5】:

      像这样保留尾随零是 .NET 1.1 中引入的,以更严格地符合 ECMA CLI 规范。

      在 MSDN 上有一些关于此的信息,例如here.

      您可以按如下方式调整精度:

      • Math.Round(或天花板、地板等)以降低精度(b 从 c)

      • 乘以 1.000...(加上您想要的小数位数)以提高精度 - 例如乘以 1.0M 从 a 得到 b。

      【讨论】:

      • 也可以用1.000... 以降低精度。
      【解决方案6】:

      很容易将 SQL Server 中的 decimal 与 .NET 中的 decimal 混淆;它们完全不同。

      SQL Server decimal 是一个定点数,其精度和小数位数在定义列或变量时是固定的。

      .NET decimal 是一个浮点数,例如 floatdouble(区别在于 decimal 准确地保留十进制数字,而 floatdouble 准确地保留二进制数字)。试图控制 .NET decimal 的精度是没有意义的,因为无论是否存在填充零,所有计算都会产生相同的结果。

      【讨论】:

      • 有趣的是,在 EF 中将构面设置为小数会创建一个正确的SQL decimal,当将c# decimal 交给它时,它会立即失败......在我的情况下,我使用了 Precision: 2, Scale: (无),由于值超出范围,我无法将800 分配给它
      【解决方案7】:

      这将从小数点中删除所有尾随零,然后您可以使用ToString()

      public static class DecimalExtensions
      {
          public static Decimal Normalize(this Decimal value)
          {
              return value / 1.000000000000000000000000000000000m;
          }
      }
      

      或者,如果您想要一个确切数量的尾随零,比如 5,首先 Normalize(),然后乘以 1.00000m。

      【讨论】:

      • 不需要那个多个零,1 后面的 28 个零就可以了。见 cmets 至other answer
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 2017-09-25
      • 2017-04-05
      相关资源
      最近更新 更多