c# - 在 C# 中格式化带有有效数字的数字

标签 c# significant-digits

我有一些十进制数据,我正在将其推送到要查看的 SharePoint 列表中。我想根据我对具体计算的了解,限制结果数据中显示的有效数字的数量。有时它会是 3,所以 12345 会变成 12300,0.012345 会变成 0.0123。偶尔会是4或5。有什么方便的方法来处理吗?

最佳答案

参见:RoundToSignificantFigures通过“P爸爸”。
我将他的方法与我喜欢的另一种方法结合起来。

在 TSQL 中四舍五入到有效数字要容易得多,其中四舍五入方法基于四舍五入位置,而不是小数位数 - .Net math.round 就是这种情况。您可以将 TSQL 中的数字四舍五入到负数位,这将四舍五入为整数 - 因此不需要缩放。

另见 other thread . Pyrolistical 的方法不错。

问题的尾随零部分对我来说更像是一个字符串操作,所以我包含了一个 ToString() 扩展方法,它会在必要时填充零。

using System;
using System.Globalization;

public static class Precision
{
    // 2^-24
    public const float FLOAT_EPSILON = 0.0000000596046448f;

    // 2^-53
    public const double DOUBLE_EPSILON = 0.00000000000000011102230246251565d;

    public static bool AlmostEquals(this double a, double b, double epsilon = DOUBLE_EPSILON)
    {
        // ReSharper disable CompareOfFloatsByEqualityOperator
        if (a == b)
        {
            return true;
        }
        // ReSharper restore CompareOfFloatsByEqualityOperator

        return (System.Math.Abs(a - b) < epsilon);
    }

    public static bool AlmostEquals(this float a, float b, float epsilon = FLOAT_EPSILON)
    {
        // ReSharper disable CompareOfFloatsByEqualityOperator
        if (a == b)
        {
            return true;
        }
        // ReSharper restore CompareOfFloatsByEqualityOperator

        return (System.Math.Abs(a - b) < epsilon);
    }
}

public static class SignificantDigits
{
    public static double Round(this double value, int significantDigits)
    {
        int unneededRoundingPosition;
        return RoundSignificantDigits(value, significantDigits, out unneededRoundingPosition);
    }

    public static string ToString(this double value, int significantDigits)
    {
        // this method will round and then append zeros if needed.
        // i.e. if you round .002 to two significant figures, the resulting number should be .0020.

        var currentInfo = CultureInfo.CurrentCulture.NumberFormat;

        if (double.IsNaN(value))
        {
            return currentInfo.NaNSymbol;
        }

        if (double.IsPositiveInfinity(value))
        {
            return currentInfo.PositiveInfinitySymbol;
        }

        if (double.IsNegativeInfinity(value))
        {
            return currentInfo.NegativeInfinitySymbol;
        }

        int roundingPosition;
        var roundedValue = RoundSignificantDigits(value, significantDigits, out roundingPosition);

        // when rounding causes a cascading round affecting digits of greater significance, 
        // need to re-round to get a correct rounding position afterwards
        // this fixes a bug where rounding 9.96 to 2 figures yeilds 10.0 instead of 10
        RoundSignificantDigits(roundedValue, significantDigits, out roundingPosition);

        if (Math.Abs(roundingPosition) > 9)
        {
            // use exponential notation format
            // ReSharper disable FormatStringProblem
            return string.Format(currentInfo, "{0:E" + (significantDigits - 1) + "}", roundedValue);
            // ReSharper restore FormatStringProblem
        }

        // string.format is only needed with decimal numbers (whole numbers won't need to be padded with zeros to the right.)
        // ReSharper disable FormatStringProblem
        return roundingPosition > 0 ? string.Format(currentInfo, "{0:F" + roundingPosition + "}", roundedValue) : roundedValue.ToString(currentInfo);
        // ReSharper restore FormatStringProblem
    }

    private static double RoundSignificantDigits(double value, int significantDigits, out int roundingPosition)
    {
        // this method will return a rounded double value at a number of signifigant figures.
        // the sigFigures parameter must be between 0 and 15, exclusive.

        roundingPosition = 0;

        if (value.AlmostEquals(0d))
        {
            roundingPosition = significantDigits - 1;
            return 0d;
        }

        if (double.IsNaN(value))
        {
            return double.NaN;
        }

        if (double.IsPositiveInfinity(value))
        {
            return double.PositiveInfinity;
        }

        if (double.IsNegativeInfinity(value))
        {
            return double.NegativeInfinity;
        }

        if (significantDigits < 1 || significantDigits > 15)
        {
            throw new ArgumentOutOfRangeException("significantDigits", value, "The significantDigits argument must be between 1 and 15.");
        }

        // The resulting rounding position will be negative for rounding at whole numbers, and positive for decimal places.
        roundingPosition = significantDigits - 1 - (int)(Math.Floor(Math.Log10(Math.Abs(value))));

        // try to use a rounding position directly, if no scale is needed.
        // this is because the scale mutliplication after the rounding can introduce error, although 
        // this only happens when you're dealing with really tiny numbers, i.e 9.9e-14.
        if (roundingPosition > 0 && roundingPosition < 16)
        {
            return Math.Round(value, roundingPosition, MidpointRounding.AwayFromZero);
        }

        // Shouldn't get here unless we need to scale it.
        // Set the scaling value, for rounding whole numbers or decimals past 15 places
        var scale = Math.Pow(10, Math.Ceiling(Math.Log10(Math.Abs(value))));

        return Math.Round(value / scale, significantDigits, MidpointRounding.AwayFromZero) * scale;
    }
}

关于c# - 在 C# 中格式化带有有效数字的数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/158172/

相关文章:

r - 如何确定 R 中数据的有效数字位数?

matlab - 四舍五入到 n 位有效数字

python - 如何创建具有给定有效数字位数的decimal.Decimal对象?

java - 根据 BigDecimals 的有效数字比较 BigDecimals 是否接近可接受

c# - 为什么 BasicHttpBinding 不需要主体名称就可以在 IIS7 上使用 Kerberos Auth?

c# - 查找并验证所有自动映射器映射

c# - 使用状态模式的分层状态机的最佳实践是什么?

c# - 创建命名空间 -> 类 -> 嵌套类?或者

c# - SocketAsyncEventArgs 缓冲区充满了零

python - 在python中将列表保存为带有两位小数的csv?