如何在const领域使用科学记数法?

时间:2013-06-17 15:49:32

标签: c#

我在数学库中工作,由于使用double的固有麻烦,我将所有相等比较类型a == b编码为Math.Abs(a - b) <= epsilon

此外,默认情况下,我希望以最大考虑精度生成格式化的字符串。也就是说,如果 epsilon 0.001,我希望我的默认格式为N3

很高兴我做了以下事情:

public static class Math3D
{
     internal const int ROUND = 3;
     public const double Epsilon = 1e-ROUND;
}

...我收到了编译错误。显然这是不允许的。

有了这个限制,我发现我无法将两个相互依赖的常量定义为consts。显然我可以将Epsilon定义为只读字段,但我觉得这样做在概念上是错误的。我错过了一种明显的方法吗?

4 个答案:

答案 0 :(得分:10)

如果您可能正在更改它,那么应该在此使用readonlyconst应该用于永远不会更改的内容,例如π。造成这种情况的原因是constreadonly之间存在细微差别。

主要问题是,如果更改const的值,则必须重新编译使用const所有依赖客户端,否则你可以shoot yourself in the foot, badly。因此,对于可能更改的值,请勿使用const,请使用readonly

因此,如果永远的值不会改变,只需使用const,然后不用担心根据Epsilon定义ROUND,只是说:

internal const int ROUND = 3;
public const double Epsilon = 1e-3;

如果你真的想确保在不改变另一个的情况下不小心改变一个,你可以在构造函数中添加一个小支票:

if (Epsilon != Math.Pow(10, -ROUND)) {
    throw new YouForgotToChangeBothConstVariablesException();
}

您甚至可以添加条件编译,以便仅在调试版本中进行编译。

如果 要更改,请使用static readonly

internal readonly int ROUND = 3;
public static readonly double Epsilon = Math.Pow(10, -ROUND);
  

有了这个限制,我发现我无法将两个相互依赖的常量定义为consts。 [...]我错过了一个明显的方法来做到这一点吗?

不,您需要使用Math.PowMath.Log进行某种数学运算,以便在ROUNDEpsilon之间进行,并且这些数学运算不适用于编译时使用const。您可以编写一个微型代码生成器,根据单个输入值吐出这两行代码,但我真的质疑投入时间的价值。

答案 1 :(得分:2)

1e-ROUND,特别是1e不是有效的文字整数。你必须做类似的事情,

public static readonly double Epsilon = 
    decimal.Parse(
        string.Format("1E-{0}", ROUND), 
        System.Globalization.NumberStyles.Float);

另外,请注意static readonly,因为在运行时之前无法知道表达式时无法使用const。在这种情况下,static readonly的工作方式与const类似。

如果您不想处理string,可以随时使用

public static readonly double Epsilon = Math.Pow(10, -ROUND);

答案 2 :(得分:1)

你总是可以硬编码3.看到你正在使用常量,那么无意将值改为3以外的任何东西吗?所以你不必过分担心DRY。

public static class Math3D
{
    internal const int ROUND = 3;
    public const double Epsilon = 1e-3;
}

如果您认为自己可能想要更改3,那么const不适合您,您的问题就没有用了。

答案 3 :(得分:1)

修改

这不是您问题的直接答案,但您是否考虑过将RoundEpsilon更改为可写字段?如果您使用这些进行格式化/舍入,几乎可以保证他们有时需要更改 - constreadonly字段都不适用。

public static class Math3D
{
    internal static int s_Round;
    internal static double s_Epsilon;

    static Math3D ()
    {
        Round = 3;
    }

    public static double Epsilon
    {
        get
        {
            return ( s_Epsilon );
        }
    }

    public static int Round
    {
        get
        {
            return ( s_Round );
        }
        set
        {
            // TODO validate
            s_Round = value;
            s_Epsilon = Math.Pow ( 10, -s_Round );
        }
    }
}

这是一个清晰可读的解决方案,在您将来改变时不会破坏。