将C#双精度浮点数或浮点数与指定的默认值进行比较是否安全?
例如:
public class Foo
{
private double defaultEfficiency = 100.0;
public double efficiencyBar = defaultEfficiency;
public bool IsBarAtDefaultValue()
{
if(efficiencyBar == defaultEfficiency)
return true;
else
return false;
}
}
所以我的问题是,如果IsBarAtDefaultValue()
内的支票能按预期运作吗?即。如果true
与efficiencyBar
相同,则会返回defaultEfficiency
。
这个问题:Is it safe to check floating ...在默认值为0.0时询问特定情况。我的问题涉及任何默认值的更广泛的情况。
提供更具体的例子......
我正在开发一种处理电机效率的应用程序,该应用程序通常在0-100%或0.0到1.0的范围内。用户可以定义新电机并分配各种效率。
在定义新电机的面板中,我想用默认值填充各种效率。稍后,我想检查并查看用户是否将值更改为默认值以外的值。例如,我想检查并查看他们是否进行了更改,但却意外忘记保存他们的工作。
这导致我想知道在为浮点类型(double& float)分配默认值时实际使用了什么值。 linked SO question讨论了0.0的情况,但我想知道更广泛的(不是0.0)情况,因为我不想使用0.0作为效率的默认值。
答案 0 :(得分:2)
是的,这样做是安全的。请考虑您的代码只是这样做:
double x = ...;
double y = x;
bool t = (x == y); //always true regardless of the value of x
答案 1 :(得分:2)
嗯,是的,不是。如果要比较的所有浮点值都存储为常量,并且常量是相同的数据宽度(即,将float
与float
和double
与{{1}进行比较}),这是一个安全的操作。但是,如果你所比较的一切都是常数,为什么不使用整数或枚举?
通常,比较浮点数是否相等是不安全的。这样做的原因是浮点数不能完美地存储所有值。这很像十进制数1/3的问题,我们必须将其写为0.33333 ...存在以二进制形式存储小数部分的相同问题,并且不能保证在十进制表示法中具有有限表示的数字有有限的二进制表示。由于我们限制为32位或64位,因此部分数字会被截断。这意味着对浮点数执行数学运算可能会导致意外后果。
从布鲁斯·M·布什的this post开始考虑这句话:
许多奇怪结果的核心是一个基本原因: 计算机上的浮点数通常是2,而外部 代表性是基数10.我们预计1/3不会完全正确 可以表示,但看起来很直观.01会。不是这样! 0.01 在IEEE单精度格式中正好是10737418/1073741824或 约0.009999999776482582。
通常应该使用一些小的epsilon来检查浮点值的相等性。
double
您可以使用Double.Epsilon
之类的内容来获取public class Foo
{
//Choose a small value that is appropriate for your needs
//see the info below for some info from Microsoft
private static double epsilon = 0.00001;
private double defaultEfficiency = 100.0;
public double efficiencyBar = defaultEfficiency;
public bool IsBarAtDefaultValue()
{
//we use the absolute value of the difference. If this is smaller than
//epsilon, then the value is "good enough" for equal
if (Math.Abs(efficiencyBar - defaultEfficiency) < epsilon)
return true;
else
return false;
}
}
值,但这可能太小符合您的需求,建议不要在文档中:
如果您创建一个自定义算法来确定是否可以将两个浮点数视为相等,我们建议您不要将算法基于Epsilon常量的值,以确定两个值的可接受的绝对差值。被认为是平等的。 (通常,差异幅度比Epsilon大很多倍。)
在他们关于Double.Equals()
方法的文档中:
因为Epsilon定义了范围接近零的正值的最小表达式,所以两个相似值之间的差值边界必须大于Epsilon。通常情况下,它比Epsilon大很多倍。因此,我们建议您在比较Double值的相等性时不要使用Epsilon。
这两个地方都是安全比较浮点数的附加信息的良好来源。
答案 2 :(得分:0)
因为efficiencyBar
是公共的并且可以在类之外进行修改,所以您不知道它可以设置为什么,以及浮点预设问题是否会在运行时依赖于指定的新值。由于浮点精度问题,最好与epsilon
值进行比较。
return (Math.Abs(efficiencyBar - defaultEfficiency) < Double.Epsilon);
答案 3 :(得分:0)
如果主要目标是检测值是否从默认/初始值更改,则可以定义一个自定义设置器来跟踪值是否曾被更改。
public class Foo
{
private double defaultEfficiency = 100.0;
private double _efficiencyBar = defaultEfficiency;
public double efficiencyBar
{
get
{
return _efficiencyBar;
}
set
{
_efficiencyBar = value;
_efficiencyBarChanged = true;
}
}
private bool _efficiencyBarChanged = false;
//Now you know if it was ever changed, period
//even if it got changed back to the default value
public bool IsBarAtDefaultValue
{
get
{
return !_efficiencyBarChanged;
//if you preferred, this could still be an equality-like test
//but keeping track of a state change in a bool value makes more sense to me
}
}
}
如果您希望能够将值“重置”为默认值并让您的支票返回false
,我建议使用以下方法:
public void resetEfficiencyBar()
{
_efficiencyBar = defaultEfficiency;
_efficiencyBarChanged = false;
}
这避免了与浮点比较相关的所有复杂问题,我认为使代码的意图更加清晰。
答案 4 :(得分:0)
请注意,所有大小小于或等于2 53 +1的整数完全在IEEE754双精度中可表示,因此您的{在您IsBarAtDefaultValue
等于defaultEfficiency
的情况下,{1}}函数将完全符合您的预期。