4

我正在尝试制作一个自适应的“大约相等”方法(用 C# 编写,但问题很笼统)接受两个双精度数并返回一个布尔值,如果它们“大约相等”或不“大约相等”。通过自适应,我的意思是:

1.234 和 1.235 ==> 真

1.234567 和 1.234599 ==> 错误

也就是说,“大约相等”的精度适应数字的精度。

我在如何找到两个变量是否近似相等?但是对于 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)

无论实现代码如何,我都假设会有某种“公差”变量来确定什么是“非常不同”和什么是“接近”。

4

3 回答 3

5

比较浮点数的一种方法是比较有多少浮点表示将它们分开。该解决方案与数字的大小无关,因此您不必担心“epsilon”。

该算法的描述可以在这里找到(最后是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;
    }
}

如果您想比较浮点数,请将 all 更改double为、floatall和longto 。int0x80000000000000000x80000000

于 2012-05-02T19:00:43.867 回答
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 之类的数字。在这种情况下,如果两个数字的范围重叠,您会将它们视为大致相等(尽管实际上,这会使您的相等运算意味着“可能相等”而您的不等式运算意味着“绝对不相等”。

另见,区间算术

于 2012-05-03T07:22:50.143 回答
0

您可以将一个除以另一个,看看结果是否接近一,例如

var ratio = a / b;
var diff = Math.Abs(ratio - 1);
return diff <= epsilon;

然后你只需要选择你的 epsilon 来决定这些值必须有多接近。

但是,在您的示例中,您的第一个示例相差 0.08%,但第二个示例相差 0.003%,但您希望第一个为真,第二个为假。我不确定您真正在寻找什么,您需要先了解这一点,然后才能决定问题的解决方案。(需要正确的问题才能得到正确的答案)您可能会想象“最后一个有效数字可以不同,但​​不会更多”,但用数学术语定义它并不那么简单。例如,0.49999999997 是否应该等于 0.5?那么0.500000000003呢?比较这些数字有什么意义?

于 2012-05-02T18:48:27.130 回答