我试图制作一个自适应的“约等于”方法(用C#编写,但问题是一般的)接受两个双精度并返回一个布尔值,如果它们是“大约相等”或不是。通过自适应,我的意思是:
1.234和1.235 ==> TRUE
BUT
1.234567和1.234599 ==> FALSE
也就是说,“约等于”的准确性适应数字的准确性。
我在How do I find if two variables are approximately equals?找到了四舍五入的概念,但仍然有一个关于如何用于epsilon的开放式问题。
是否有人了解此类问题的最佳做法? 提前谢谢!
编辑: 我最初的问题没有包含足够的信息,我想要得到什么。抱歉,我的道歉。我想要一个程序,它可以将更高的精确度数字处理到更高的标准,同时对更低的精确度数字更宽松。对的更多例子是(其中'(0)'是隐含的零):
1.077和1.07(0)返回false(因为77与70非常不同)
1.000077和1.00007(0)返回false(因为77与70非常不同)
1.071和1.07(0)返回true(因为71接近70
1.000071和1.00007(0)返回true(因为71接近70)
无论实施代码如何,我都假设会有某种“容差”变量来确定什么是“非常不同”和什么是“接近”。
答案 0 :(得分:5)
比较浮点数的一种方法是比较将它们分开的浮点表示的数量。这个解决方案对数字的大小无动于衷,因此您不必担心“epsilon”。
可以找到算法的描述here(最后是AlmostEqual2sComplement函数),这是我的C#版本。
public static class DoubleComparerExtensions
{
public static bool AlmostEquals(this double left, double right, long representationTolerance)
{
long leftAsBits = left.ToBits2Complement();
long rightAsBits = right.ToBits2Complement();
long floatingPointRepresentationsDiff = Math.Abs(leftAsBits - rightAsBits);
return (floatingPointRepresentationsDiff <= representationTolerance);
}
private static unsafe long ToBits2Complement(this double value)
{
double* valueAsDoublePtr = &value;
long* valueAsLongPtr = (long*)valueAsDoublePtr;
long valueAsLong = *valueAsLongPtr;
return valueAsLong < 0
? (long)(0x8000000000000000 - (ulong)valueAsLong)
: valueAsLong;
}
}
如果您想比较花车,请将所有double
更改为float
,将所有long
更改为int
,将0x8000000000000000
更改为0x80000000
答案 1 :(得分:4)
float
和double
没有精确的思维方式。通过截断尾随零来编程伪精度......但是你不能将这个技巧用于你的目的,因为舍入错误会阻止这种技巧的可靠性。
decimal
确实跟踪小数点右边有多少位数,但这对于实现您建议的算法仍然毫无价值,因为任何引入的算法都是如此。表示性错误(例如,除以3)将倾向于最大化小数点右边的位数。
如果你想根据数据的已知精度实际具有模糊相等性,那么一种方法就是创建自己的数字类。像这样:
public class DoublePrecise
{
public readonly double Error;
public readonly double Value;
public DoublePrecise(double error, double value) {
Error = error;
Value = value;
}
public static DoublePrecise operator -(DoublePrecise d1, DoublePrecise d2) {
return new DoublePrecise(d1.Value - d2.Value, d1.Error + d2.Error);
}
//More stuff.
}
基本上,这是让你的代表数字像10.0±0.1。在这种情况下,如果它们的范围重叠,你会认为两个数字大致相等(尽管实际上,这会使你的平等操作意味着“可能相等”而你的不等式操作意味着“绝对不相等。”
答案 2 :(得分:0)
您可以将另一个分开,看看结果是否接近1,例如
var ratio = a / b;
var diff = Math.Abs(ratio - 1);
return diff <= epsilon;
然后你必须选择你的epsilon来决定值必须接近的程度。
但是,在您的示例中,您的第一个示例是0.08%的差异,但第二个示例是0.003%的差异,但您希望第一个为真,第二个为假。我不确定你在寻找什么,在确定问题的解决方案之前,你需要知道。 (在你能得到正确的答案之前需要正确的问题)你可能会想象“最后一个有效数字可以有所不同,但不会有更多”,但用数学术语来定义并不是那么简单。例如。应该0.49999999997等于0.5?怎么到0.500000000003?比较这些数字有什么意义?