【问题标题】:Determine the decimal precision of an input number确定输入数字的小数精度
【发布时间】:2010-07-19 14:17:21
【问题描述】:

我们有一个有趣的问题,我们需要确定用户输入(文本框)的小数精度。本质上我们需要知道输入的小数位数,然后返回一个精度数,最好用例子来说明:

输入 4500 将产生结果 1
输入 4500.1 将产生结果 0.1
输入 4500.00 将产生结果 0.01
输入 4500.450 将产生结果 0.001

我们正在考虑使用字符串,找到小数点分隔符,然后计算结果。只是想知道是否有更简单的解决方案。

【问题讨论】:

  • 你确定 4500 是 1,而不是 100?
  • 情况 0.0 和 0.1 的预期结果是什么?
  • 对我来说这是一个令人困惑的问题。您不要求小数精度。你问:如何获得小数部分的位数。 “小数精度”的定义在这里为我提供:docs.oracle.com/cd/B28359_01/server.111/b28318/…

标签: c# decimal precision decimal-point


【解决方案1】:

我认为你应该按照你的建议去做 - 使用小数点的位置。明显的缺点可能是您必须自己考虑国际化。

var decimalSeparator = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;

var position = input.IndexOf(decimalSeparator);

var precision = (position == -1) ? 0 : input.Length - position - 1;

// This may be quite unprecise.
var result = Math.Pow(0.1, precision);

您可以尝试另一件事 - Decimal 类型存储一个内部精度值。因此,您可以使用 Decimal.TryParse() 并检查返回值。也许解析算法保持了输入的精度。

最后我建议不要尝试使用浮点数。仅解析输入将删除有关尾随零的任何信息。所以你必须添加一个人为的非零数字来保存它们或做类似的把戏。您可能会遇到精度问题。最后根据浮点数找到精度也并不简单。我看到一些丑陋的数学或循环每次迭代都乘以十,直到不再有任何小数部分。并且循环带来了新的精度问题......

更新

解析成十进制有效。详情请咨询Decimal.GetBits()

var input = "123.4560";

var number = Decimal.Parse(input);

// Will be 4.
var precision = (Decimal.GetBits(number)[3] >> 16) & 0x000000FF;

从这里使用Math.Pow(0.1, precision) 是直截了当的。

更新 2

使用 decimal.GetBits() 将分配一个 int[] 数组。如果您想避免分配,可以使用以下辅助方法,该方法使用显式布局结构直接从十进制值中获取比例:

static int GetScale(decimal d)
{
    return new DecimalScale(d).Scale;
}

[StructLayout(LayoutKind.Explicit)]
struct DecimalScale
{
    public DecimalScale(decimal value)
    {
        this = default;
        this.d = value;
    }

    [FieldOffset(0)]
    decimal d;

    [FieldOffset(0)]
    int flags;

    public int Scale => (flags >> 16) & 0xff;
}

【讨论】:

  • +1 对于decimal.Precision,我不知道它可以做到这一点:) 见stackoverflow.com/questions/1132765/…
  • 由于 OP 要求“更简单的解决方案”,并且可能推断“性能更好”,这与仅解析字符串相比在性能方面如何?
  • 你在评论中说“将是4”,但是“123.4560”的精度应该是7。比例应该是4。
  • 123.4560 有没有得到精度=3?因为最后的 0 不应该算?
【解决方案2】:

只是想知道是否有更简单的 解决这个问题。

没有。

使用字符串:

string[] res = inputstring.Split('.');
int precision = res[1].Length;

【讨论】:

【解决方案3】:

由于您的最后一个示例表明尾随零很重要,因此我会排除任何数值解决方案并进行字符串操作。

【讨论】:

    【解决方案4】:

    不,没有更简单的解决方案,您必须检查字符串。如果将"4500""4500.00" 转换为数字,它们都会变成值4500,因此您无法分辨小数分隔符后面有多少个非值数字。

    【讨论】:

      【解决方案5】:

      有趣的是,Decimal 试图保持用户输入的精度。例如,

      Console.WriteLine(5.0m);
      Console.WriteLine(5.00m);
      Console.WriteLine(Decimal.Parse("5.0"));
      Console.WriteLine(Decimal.Parse("5.00"));
      

      有输出:

      5.0
      5.00
      5.0
      5.00
      

      如果您跟踪输入精度的动机纯粹是出于输入和输出原因,那么这可能足以解决您的问题。

      【讨论】:

      • 查看 Daniel Bruckner 的回答,了解如何直接访问此信息。
      【解决方案6】:

      使用字符串很容易。

      如果没有“.”在字符串中,返回 1。

      否则返回“0.”,后跟n-1个“0”,后跟一个“1”,其中n是小数点后字符串的长度。

      【讨论】:

      • 警告:首先确保字符串是有效数字/最后一个字符不是“.”
      【解决方案7】:

      这是一个使用字符串的可能解决方案;

      static double GetPrecision(string s)
      { 
          string[] splitNumber = s.Split('.');
          if (splitNumber.Length > 1)
          {
              return 1 / Math.Pow(10, splitNumber[1].Length);
          }
          else
          {
              return 1;
          }
      }
      

      这里有个问题; Calculate System.Decimal Precision and Scale 如果您想深入研究一下,这看起来可能会很有趣。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多