当前位置:  开发笔记 > 编程语言 > 正文

将双倍数换算为x有效数字

如何解决《将双倍数换算为x有效数字》经验,为你挑选了5个好方法。

如果我有一个双(234.004223)等,我想将其舍入为C#中的x位有效数字.

到目前为止,我只能找到舍入到x小数位的方法,但如果数字中有任何0,则只会删除精度.

例如,0.086到一位小数位变为0.1,但我希望它保持在0.08.



1> P Daddy..:

该框架没有内置函数来将(或截断,如在您的示例中)舍入为多个有效数字.但是,您可以采用的一种方法是缩放数字,使您的第一个有效数字位于小数点后面,圆形(或截断),然后缩小.以下代码应该可以解决问题:

static double RoundToSignificantDigits(this double d, int digits){
    if(d == 0)
        return 0;

    double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);
    return scale * Math.Round(d / scale, digits);
}

如果在您的示例中,您确实要截断,那么您需要:

static double TruncateToSignificantDigits(this double d, int digits){
    if(d == 0)
        return 0;

    double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1 - digits);
    return scale * Math.Truncate(d / scale);
}


@leftbrainlogic:是的,确实如此:http://msdn.microsoft.com/en-us/library/75ks3aby.aspx
这些方法都不适用于负数,因为如果d <0,Math.Log10将返回Double.NaN.
@PDaddy嗯,你需要检查d == 0,因为这也会导致Double.NaN - 两种方法都需要一些保护条款,例如:if(d == 0){return 0; } if(d <0){d = Math.Abs​​(d); } - 否则你最终会得到0的除法.
@Fraser:嗯,为读者进行练习.顺便说一下,埃里克注意到(http://stackoverflow.com/a/1925170/36388)两年前的负面数字缺陷(不是零缺点).也许我应该修改这段代码,以便人们不再打电话给我.
@PDaddy是的,请修复它。如果它是固定的,我会+1。我猜很多人错误地将高度投票的答案视为可复制粘贴。

2> Eric..:

我一直在使用pDaddy的sigfig功能几个月,并发现了它的一个错误.您不能记录负数的对数,因此如果d为负数,则结果为NaN.

以下更正了错误:

public static double SetSigFigs(double d, int digits)
{   
    if(d == 0)
        return 0;

    decimal scale = (decimal)Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);

    return (double) (scale * Math.Round((decimal)d / scale, digits));
}


失败(0.073699979,7)返回`0.073699979999999998`

3> Jon Skeet..:

这听起来像你根本不想舍入到x小数位 - 你想要舍入到x有效数字.因此,在您的示例中,您希望将0.086舍入为一个有效数字,而不是一个小数位.

现在,由于存储双精度的方式,使用双精度和舍入到多个有效数字是有问题的.例如,您可以将0.12舍入到接近 0.1的值,但0.1不能完全表示为double.你确定你不应该使用小数吗?或者,这实际上是用于显示目的吗?如果是出于显示目的,我怀疑你应该将double直接转换为具有相关有效位数的字符串.

如果你能回答这些问题,我可以尝试提出一些适当的代码.听起来很糟糕,通过将数字转换为"完整"字符串然后找到第一个有效数字(然后在此之后采取适当的舍入操作)转换为多个有效数字作为字符串可能是最好的方法.



4> farfareast..:

如果它是出于显示目的(正如你在对Jon Skeet的回答的评论中所述),你应该使用Gn 格式说明符.其中n是有效位数 - 正是你所追求的.

如果您想要3位有效数字(打印输出位于每行的注释中),以下是使用示例:

    Console.WriteLine(1.2345e-10.ToString("G3"));//1.23E-10
    Console.WriteLine(1.2345e-5.ToString("G3")); //1.23E-05
    Console.WriteLine(1.2345e-4.ToString("G3")); //0.000123
    Console.WriteLine(1.2345e-3.ToString("G3")); //0.00123
    Console.WriteLine(1.2345e-2.ToString("G3")); //0.0123
    Console.WriteLine(1.2345e-1.ToString("G3")); //0.123
    Console.WriteLine(1.2345e2.ToString("G3"));  //123
    Console.WriteLine(1.2345e3.ToString("G3"));  //1.23E+03
    Console.WriteLine(1.2345e4.ToString("G3"));  //1.23E+04
    Console.WriteLine(1.2345e5.ToString("G3"));  //1.23E+05
    Console.WriteLine(1.2345e10.ToString("G3")); //1.23E+10


虽然接近,但这并不总是返回sigfigs ...例如,`G4`会从`1.000` - >`1`中删除零.此外,无论你喜不喜欢,它都会自行决定科学记谱法.

5> 小智..:

我在P爸爸和埃里克的方法中发现了两个错误.这解决了例如Andrew Hancox在本问答中提出的精度误差.圆方向也存在问题.有两个有效数字的1050不是1000.0,而是1100.0.使用MidpointRounding.AwayFromZero修正了舍入.

static void Main(string[] args) {
  double x = RoundToSignificantDigits(1050, 2); // Old = 1000.0, New = 1100.0
  double y = RoundToSignificantDigits(5084611353.0, 4); // Old = 5084999999.999999, New = 5085000000.0
  double z = RoundToSignificantDigits(50.846, 4); // Old = 50.849999999999994, New =  50.85
}

static double RoundToSignificantDigits(double d, int digits) {
  if (d == 0.0) {
    return 0.0;
  }
  else {
    double leftSideNumbers = Math.Floor(Math.Log10(Math.Abs(d))) + 1;
    double scale = Math.Pow(10, leftSideNumbers);
    double result = scale * Math.Round(d / scale, digits, MidpointRounding.AwayFromZero);

    // Clean possible precision error.
    if ((int)leftSideNumbers >= digits) {
      return Math.Round(result, 0, MidpointRounding.AwayFromZero);
    }
    else {
      return Math.Round(result, digits - (int)leftSideNumbers, MidpointRounding.AwayFromZero);
    }
  }
}

推荐阅读
保佑欣疼你的芯疼
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有