如果我有一个双(234.004223)等,我想将其舍入为C#中的x位有效数字.
到目前为止,我只能找到舍入到x小数位的方法,但如果数字中有任何0,则只会删除精度.
例如,0.086到一位小数位变为0.1,但我希望它保持在0.08.
该框架没有内置函数来将(或截断,如在您的示例中)舍入为多个有效数字.但是,您可以采用的一种方法是缩放数字,使您的第一个有效数字位于小数点后面,圆形(或截断),然后缩小.以下代码应该可以解决问题:
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); }
我一直在使用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)); }
这听起来像你根本不想舍入到x小数位 - 你想要舍入到x有效数字.因此,在您的示例中,您希望将0.086舍入为一个有效数字,而不是一个小数位.
现在,由于存储双精度的方式,使用双精度和舍入到多个有效数字是有问题的.例如,您可以将0.12舍入到接近 0.1的值,但0.1不能完全表示为double.你确定你不应该使用小数吗?或者,这实际上是用于显示目的吗?如果是出于显示目的,我怀疑你应该将double直接转换为具有相关有效位数的字符串.
如果你能回答这些问题,我可以尝试提出一些适当的代码.听起来很糟糕,通过将数字转换为"完整"字符串然后找到第一个有效数字(然后在此之后采取适当的舍入操作)转换为多个有效数字作为字符串可能是最好的方法.
如果它是出于显示目的(正如你在对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
我在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); } } }