5

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

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

很高兴我做了以下事情:

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

...我得到一个编译错误。显然这是不允许的。

有了这个限制,我看不出我可以将两个相互依赖的常量都定义为 const。显然我可以定义Epsilon为只读字段,但我觉得这样做在概念上是错误的。我是否错过了如何做到这一点的明显方法?

4

4 回答 4

10

如果你可能要改变它,你应该在这里使用readonlyconst应该真正用于永远不会改变的事物,例如 π。这是因为 和 之间的细微const差别readonly

主要问题是,如果更改 的值,则const必须重新编译所有使用 的依赖客户端const,否则您可能会自责,严重。所以对于可能改变的值,不要使用const,使用readonly

所以,如果值永远不会改变,只需使用const,然后不用担心定义EpsilonROUND,只需说:

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);

有了这个限制,我看不出我可以将两个相互依赖的常量都定义为 const。[...]我是否错过了如何做到这一点的明显方法?

Math.Pow不,您需要使用orMath.LogROUNDand和 之间进行某种数学运算Epsilon,这些对于const. 您可以编写一个微型代码生成器,根据单个输入值生成这两行代码,但我真的怀疑在其中投入时间的价值。

于 2013-06-17T15:54:45.273 回答
2

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

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

另外,请注意,static readonly因为您不能使用 a constwhen 表达式直到运行时才知道。在这种情况下,它的static readonly工作方式与 a 类似const

如果你不想处理strings,你总是可以这样做,

public static readonly double Epsilon = Math.Pow(10, -ROUND);
于 2013-06-17T15:55:18.493 回答
1

您总是可以对 3 进行硬编码。看到您正在使用常量,那么就没有打算将值更改为 3 以外的任何值,对吧?所以你不需要太担心 DRY。

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

如果您认为您可能想要更改 3,那么const不适合您,您的问题变得没有实际意义。

于 2013-06-17T15:58:18.163 回答
1

编辑:

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

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 );
        }
    }
}

这是一个清晰易读的解决方案,在您将来更改内容时不会中断。

于 2013-06-17T16:05:03.557 回答