我有一个名为double
的{{1}}变量。
在代码中,x
被分配了x
的值,我在比较0.1
和x
0.1
不幸的是它没有输入if (x==0.1)
{
----
}
声明
我应该使用if
还是Double
?
这背后的原因是什么?你能为此建议一个解决方案吗?
答案 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/3
为0.3333333…
。同样的事情以二进制表示法发生,除了不能精确表示的数字是不同的。其中有数字1/10
。以二进制表示法0.000110011001100…
。
由于二进制表示法无法精确存储,因此它以四舍五入的方式存储。因此你的问题。
答案 1 :(得分:35)
double
和Double
相同(double
是Double
的别名),可以互换使用。
将double与另一个值进行比较的问题是,double是近似值,而不是精确值。因此,当您将x
设置为0.1
时,它实际上可能会存储为0.100000001
或类似的内容。
不应检查是否相等,而应检查差异是否小于定义的最小差异(容差)。类似的东西:
if (Math.Abs(x - 0.1) < 0.0000001)
{
...
}
答案 2 :(得分:16)
您需要Math.Abs
X-Y
与value
的组合进行比较。
您可以使用以下扩展方法方法
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方法,我相信它非常安全。
然后您可以将x
和y
比较为
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)
比较精度 应谨慎使用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 ???
Double
和double
是一回事。 double
只是一个C#关键字,用作类System.Double
的别名
最常见的是使用别名! string
(System.String
),int
(System.Int32
)
答案 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;
}