I'm working in a math library, and due to the inherent troubles of working with double
I am coding all equality comparisons type a == b
as Math.Abs(a - b) <= epsilon
.
Also, by default, I want my formatted strings to be generated with the maximum considered precision. That is, if epsilon is 0.001
I want my default format to be N3
.
Happily I did the following:
public static class Math3D
{
internal const int ROUND = 3;
public const double Epsilon = 1e-ROUND;
}
...and I got a compilation error. Apparently this is not allowed.
With this limitation I see no way I can define both interdependant constants as consts. Obviously I can define Epsilon
as a readonly field but I feel doing so is somehow conceptually wrong. Am I missing an obvious way of how to do this?
If you're going to possibly be changing it, you should use readonly
here. const
should really be used for things that will never change, like π. The reason for this is because of a subtle difference between const
and readonly
.
The main issue is that if you change the value of the const
, you must recompile all dependent clients that use the const
, otherwise you can shoot yourself in the foot, badly. So for values that might change, don't use const
, use readonly
.
So, if the value is never going to change, just use const
and then don't worry about defining Epsilon
in terms of ROUND
, just say:
internal const int ROUND = 3;
public const double Epsilon = 1e-3;
If you really want to make sure you don't accidentally change one without changing the other, you could add a small check in the constructor:
if (Epsilon != Math.Pow(10, -ROUND)) {
throw new YouForgotToChangeBothConstVariablesException();
}
You could even add conditional compilation so that only gets compiled in debug releases.
If it is going to change, use static readonly
:
internal readonly int ROUND = 3;
public static readonly double Epsilon = Math.Pow(10, -ROUND);
With this limitation I see no way I can define both interdependant constants as consts. [...] Am I missing an obvious way of how to do this?
No, you need to do some kind of math using Math.Pow
or Math.Log
to go between ROUND
and Epsilon
and those are not acceptable for compile-time usage with const
. You could write a miniature code generator to spit out these two lines of code based on a single input value, but I really question the value of investing time into that.
1e-ROUND
, specifically 1e
is not a valid literal integer. You would have to do something like,
public static readonly double Epsilon =
decimal.Parse(
string.Format("1E-{0}", ROUND),
System.Globalization.NumberStyles.Float);
Also, note the static readonly
since you cannot use a const
when the expression won't be known until runtime. The static readonly
will work similarly to a const
in this scenario.
If you prefer not dealing with string
s, you can always do,
public static readonly double Epsilon = Math.Pow(10, -ROUND);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With