比较C#中的double值

时间:2009-09-09 10:14:26

标签: c# .net double

我有一个名为double的{​​{1}}变量。 在代码中,x被分配了x的值,我在比较0.1x

的'if'语句中进行检查
0.1

不幸的是它没有输入if (x==0.1) { ---- } 声明

  1. 我应该使用if还是Double

  2. 这背后的原因是什么?你能为此建议一个解决方案吗?

19 个答案:

答案 0 :(得分:86)

由于计算机存储浮点值的原因,这是一个标准问题。在这里搜索“浮点问题”,你会发现大量的信息。

简而言之 - float / double无法精确存储0.1。它总会有一点点。

您可以尝试使用以{十进制表示法存储数字的decimal类型。因此0.1可以准确表示。


你想知道原因:

Float / double存储为二进制分数,而不是小数分数。举例说明:

十进制表示法中的

12.34(我们使用的)意味着

1 * 101 + 2 * 100 + 3 * 10-1 + 4 * 10-2

计算机以相同的方式存储浮点数,但使用基数2除外:10.01表示

1 * 21 + 0 * 20 + 0 * 2-1 + 1 * 2-2

现在,您可能知道有些数字无法用十进制表示法完全表示。例如,十进制表示法中的1/30.3333333…。同样的事情以二进制表示法发生,除了不能精确表示的数字是不同的。其中有数字1/10。以二进制表示法0.000110011001100…

由于二进制表示法无法精确存储,因此它以四舍五入的方式存储。因此你的问题。

答案 1 :(得分:35)

doubleDouble相同(doubleDouble的别名),可以互换使用。

将double与另一个值进行比较的问题是,double是近似值,而不是精确值。因此,当您将x设置为0.1时,它实际上可能会存储为0.100000001或类似的内容。

不应检查是否相等,而应检查差异是否小于定义的最小差异(容差)。类似的东西:

if (Math.Abs(x - 0.1) < 0.0000001)
{
    ...
}

答案 2 :(得分:16)

您需要Math.Abs X-Yvalue的组合进行比较。

您可以使用以下扩展方法方法

public static class DoubleExtensions
    {
        const double _3 = 0.001;
        const double _4 = 0.0001;
        const double _5 = 0.00001;
        const double _6 = 0.000001;
        const double _7 = 0.0000001;

        public static bool Equals3DigitPrecision(this double left, double right)
        {
            return Math.Abs(left - right) < _3;
        }

        public static bool Equals4DigitPrecision(this double left, double right)
        {
            return Math.Abs(left - right) < _4;
        }

        ...

由于你很少在ToString之外调用double方法,我相信它非常安全。

然后您可以将xy比较为

if(x.Equals4DigitPrecision(y))

答案 3 :(得分:10)

由于四舍五入,不能总是精确地比较浮点数。比较

(x == .1)

计算机真的比较

(x - .1) vs 0

由于机器上如何表示浮点数,因此无法始终精确地表示缩减结果。因此,您获得一些非零值,条件评估为false

要克服这种比较

Math.Abs(x- .1) vs some very small threshold ( like 1E-9)

答案 4 :(得分:5)

来自documentation

  

比较精度   应谨慎使用Equals方法,因为两个明显等效的值可能不相等,因为这两个值的精度不同。下面的示例报告Double值.3333和通过除以1而返回的Double是不相等的。

...

  

一种推荐的技术不是比较相等性,而是涉及在两个值之间定义可接受的差异裕度(例如其中一个值的0.01%)。如果两个值之间的差的绝对值小于或等于该边界,则差异可能是由于精度的差异,因此,值可能相等。以下示例使用此技术比较.33333和1/3,前两个代码示例发现的两个Double值不相等。

因此,如果你真的需要一个双倍,你应该使用文档中描述的技术。 如果可以,请将其更改为小数。 它会慢一点,但你不会遇到这类问题。

答案 5 :(得分:3)

双和双相同。

因此,请参阅http://www.yoda.arachsys.com/csharp/floatingpoint.html。 简而言之:双倍不是一个确切的类型,“x”和“0.1”之间的微小差异会将其抛弃。

答案 6 :(得分:3)

由于舍入和内部表示问题,浮点值的精确比较并不总是有效。

尝试不精确的比较:

if (x >= 0.099 && x <= 0.101)
{
}

另一种方法是使用十进制数据类型。

答案 7 :(得分:2)

使用decimal。它没有这个“问题”。

答案 8 :(得分:1)

Double(在某些语言中称为float)由于舍入问题而出现问题,只有在需要近似值时才会出现问题。

十进制数据类型可以满足您的需求。

对于参考decimal和Decimal在.NET C#中是相同的,double和Double类型都是相同的,它们都指向相同的类型(尽管如此,十进制和双精度非常不同,如您所见)。

请注意Decimal数据类型有一些与之相关的成本,因此如果您正在查看循环等,请谨慎使用它。

答案 9 :(得分:1)

  

1)我应该使用Double还是Double ???

Doubledouble是一回事。 double只是一个C#关键字,用作类System.Double的别名 最常见的是使用别名! stringSystem.String),intSystem.Int32

的情况相同

另见Built-In Types Table (C# Reference)

答案 10 :(得分:1)

从Java代码库中获取提示,尝试使用.CompareTo并测试零比较。这假设.CompareTo函数以精确的方式考虑浮点相等。例如,

System.Math.PI.CompareTo(System.Math.PI) == 0

此谓词应返回true

答案 11 :(得分:0)

作为一般规则:

在大多数情况下,双重表示足够好但在某些情况下可能会失败。如果您需要完全精确(如在财务应用程序中),请使用小数值。

双打的大多数问题不是来自直接比较,它是由于几个数学运算的积累导致的,这些运算由于舍入和分数误差(特别是乘法和除法)而指数地扰乱了值。

检查您的逻辑,如果代码是:

x = 0.1

if (x == 0.1)

它应该不会失败,它是简单的失败,如果X值是通过更复杂的手段或操作计算的,调试器使用的ToString方法很可能使用智能舍入,也许你可以这样做(如果这是太冒险了回去使用十进制):

if (x.ToString() == "0.1")

答案 12 :(得分:0)

浮点数表示是众所周知的不准确(因为浮点数在内部存储的方式),例如x实际上可能是0.0999999999或0.100000001,您的条件将失败。如果要确定浮点数是否相等,则需要具体说明它们是否等于某个容差范围内。

即。

if(Math.Abs(x) - 0.1 < tol) {
   // Do something 
}

答案 13 :(得分:0)

// number of digits to be compared    
public int n = 12 
// n+1 because b/a tends to 1 with n leading digits
public double Epsilon { get; } = Math.Pow(10, -(n+1)); 

public bool IsEqual(double a, double b)
{
    if (Math.Abs(a)<= double.Epsilon || Math.Abs(b) <= double.Epsilon)
        return Math.Abs(a - b) <=  double.Epsilon;
    return Math.Abs(1.0 - a / b) <=  Epsilon;
}

答案 14 :(得分:0)

添加到Valentin Kuzub的答案above

我们可以使用一种支持提供第n个精度数字的方法:

public static bool EqualsNthDigitPrecision(this double value, double compareTo, int precisionPoint) =>
    Math.Abs(value - compareTo) < Math.Pow(10, -Math.Abs(precisionPoint));

注意:此方法是为简化而构建的,没有增加体积,也没有考虑性能。

答案 15 :(得分:0)

要比较浮点,双精度或浮点类型,请使用CSharp的特定方法:

if (double1.CompareTo(double2) > 0)
{
    // double1 is greater than double2
}
if (double1.CompareTo(double2) < 0)
{
    // double1 is less than double2
}
if (double1.CompareTo(double2) == 0)
{
    // double1 equals double2
}

https://docs.microsoft.com/en-us/dotnet/api/system.double.compareto?view=netcore-3.1

答案 16 :(得分:0)

MS 官方帮助,尤其对问题上下文中的“比较精度”部分感兴趣。 https://docs.microsoft.com/en-us/dotnet/api/system.double.equals

// Initialize two doubles with apparently identical values
double double1 = .333333;
double double2 = (double) 1/3;
// Define the tolerance for variation in their values
double difference = Math.Abs(double1 * .00001);

// Compare the values
// The output to the console indicates that the two values are equal
if (Math.Abs(double1 - double2) <= difference)
   Console.WriteLine("double1 and double2 are equal.");
else
   Console.WriteLine("double1 and double2 are unequal.");

答案 17 :(得分:-2)

我发现很酷的黑客是使用.GetHashCode()方法返回一个表示双精度的int,即。

(0.4d + 0.3d + 0.2d + 0.1d).GetHashCode() //returns -1072693248

1d.GetHashCode() //returns 1072693248

所以你现在注意到我们可以使用这样的东西

public static bool AccurateEquality(double first,double second)
{
    return Math.Abs(first.GetHashCode()) == Math.Abs(second.GetHashCode());
}

用法: AccurateEquality((0.4d + 0.3d + 0.2d + 0.1d),1) //returns true

while:(0.4d + 0.3d + 0.2d + 0.1d) == 1d //returns false

我在多个案例上尝试过它,似乎效果很好。

答案 18 :(得分:-8)

大多数上述方式或遵循愚蠢的扩展方法!

public static bool EqualsTo(this double value, double value2)
{
    var bytes1 = BitConverter.GetBytes(value);
    var bytes2 = BitConverter.GetBytes(value2);

    var long1 = BitConverter.ToInt64(bytes1, 0);
    var long2 = BitConverter.ToInt64(bytes2, 0);

    return long1 == long2;
}