C#中的双重比较精度损失,加减双精度时发生精度损失
Double comparison precision loss in C#, accuracy loss happening when adding subtracting doubles
刚开始学习 C#。我计划将它用于繁重的数学模拟,包括数值求解。问题是我在添加和减去 double
时以及比较时会出现精度损失。代码及其内容returns(在评论中)如下:
namespace ex3
{
class Program
{
static void Main(string[] args)
{
double x = 1e-20, foo = 4.0;
Console.WriteLine((x + foo)); // prints 4
Console.WriteLine((x - foo)); // prints -4
Console.WriteLine((x + foo)==foo); // prints True BUT THIS IS FALSE!!!
}
}
}
非常感谢任何帮助和说明!
让我不解的是 (x + foo)==foo
returns True
.
查看 double
的 MSDN 参考:https://msdn.microsoft.com/en-AU/library/678hzkk9.aspx
表示double
的精度为15到16位。
但是 1e-20
和 4.0
之间的位数相差 20 位数。尝试在 4.0
中添加或减去 1e-20
的行为仅仅意味着 1e-20
丢失了,因为它不适合 15 到 16 位精度。
因此,就 double
而言,4.0 + 1e-20 == 4.0
和 4.0 - 1e-20 == 4.0
。
您要查找的可能是十进制结构 (https://msdn.microsoft.com/en-us/library/system.decimal.aspx)。
Doubles
无法以您正在寻找的精度正确表示那种值 (C# double to decimal precision loss)。相反,尝试使用 Decimal
class,像这样:
decimal x = 1e-20M, foo = 4.0M;
Console.WriteLine(Decimal.Add(x, foo)); //prints 4,0000000000000000001
Console.WriteLine(Decimal.Add(x, -foo)); //prints -3,9999999999999999999
Console.WriteLine(Decimal.Add(x, foo) == foo); // prints false
对 Enigmativity 答案的补充:
要做到这一点,您需要更高的精度,decimal
精度为 28 到 29 位,基数为 10:
decimal x = 1e-20m, foo = 4.0m;
Console.WriteLine((x + foo)); // prints 4.00000000000000000001
Console.WriteLine((x - foo)); // prints -3.99999999999999999999
Console.WriteLine((x + foo) == foo); // prints false.
但请注意,小数确实具有更大的 精度,但具有较低的 范围。查看有关十进制的更多信息 here
这不是 C# 的问题,而是您的计算机的问题。它并不复杂或难以理解,但需要阅读很长时间。如果您想真正深入了解计算机的工作原理,您应该阅读 this article。
一个优秀的 TLDR 站点 恕我直言,比上述文章更能介绍该主题 是这篇文章:
我将为您提供一个非常简短的解释,说明正在发生的事情,但您当然应该至少阅读该网站,以避免将来出现麻烦,因为您的应用领域需要深入了解此类知识。
情况如下:您有 1e-20,它比 1.11e-16 小。另一个数字在您的计算机上称为双精度机器 epsilon(最有可能)。如果您将等于或大于 1 的数字添加到小于机器 epsilon 的某个值,它将被四舍五入,回到大数字。这是由于 IEEE 754 表示。这意味着在加法发生后,结果为 "correct"(就像你有无限精度),结果以 limited/finite 精度的格式存储,舍入 4.00....001到4,因为四舍五入的误差小于1.11e-16,所以可以接受。
刚开始学习 C#。我计划将它用于繁重的数学模拟,包括数值求解。问题是我在添加和减去 double
时以及比较时会出现精度损失。代码及其内容returns(在评论中)如下:
namespace ex3
{
class Program
{
static void Main(string[] args)
{
double x = 1e-20, foo = 4.0;
Console.WriteLine((x + foo)); // prints 4
Console.WriteLine((x - foo)); // prints -4
Console.WriteLine((x + foo)==foo); // prints True BUT THIS IS FALSE!!!
}
}
}
非常感谢任何帮助和说明!
让我不解的是 (x + foo)==foo
returns True
.
查看 double
的 MSDN 参考:https://msdn.microsoft.com/en-AU/library/678hzkk9.aspx
表示double
的精度为15到16位。
但是 1e-20
和 4.0
之间的位数相差 20 位数。尝试在 4.0
中添加或减去 1e-20
的行为仅仅意味着 1e-20
丢失了,因为它不适合 15 到 16 位精度。
因此,就 double
而言,4.0 + 1e-20 == 4.0
和 4.0 - 1e-20 == 4.0
。
您要查找的可能是十进制结构 (https://msdn.microsoft.com/en-us/library/system.decimal.aspx)。
Doubles
无法以您正在寻找的精度正确表示那种值 (C# double to decimal precision loss)。相反,尝试使用 Decimal
class,像这样:
decimal x = 1e-20M, foo = 4.0M;
Console.WriteLine(Decimal.Add(x, foo)); //prints 4,0000000000000000001
Console.WriteLine(Decimal.Add(x, -foo)); //prints -3,9999999999999999999
Console.WriteLine(Decimal.Add(x, foo) == foo); // prints false
对 Enigmativity 答案的补充:
要做到这一点,您需要更高的精度,decimal
精度为 28 到 29 位,基数为 10:
decimal x = 1e-20m, foo = 4.0m;
Console.WriteLine((x + foo)); // prints 4.00000000000000000001
Console.WriteLine((x - foo)); // prints -3.99999999999999999999
Console.WriteLine((x + foo) == foo); // prints false.
但请注意,小数确实具有更大的 精度,但具有较低的 范围。查看有关十进制的更多信息 here
这不是 C# 的问题,而是您的计算机的问题。它并不复杂或难以理解,但需要阅读很长时间。如果您想真正深入了解计算机的工作原理,您应该阅读 this article。
一个优秀的 TLDR 站点 恕我直言,比上述文章更能介绍该主题 是这篇文章:
我将为您提供一个非常简短的解释,说明正在发生的事情,但您当然应该至少阅读该网站,以避免将来出现麻烦,因为您的应用领域需要深入了解此类知识。
情况如下:您有 1e-20,它比 1.11e-16 小。另一个数字在您的计算机上称为双精度机器 epsilon(最有可能)。如果您将等于或大于 1 的数字添加到小于机器 epsilon 的某个值,它将被四舍五入,回到大数字。这是由于 IEEE 754 表示。这意味着在加法发生后,结果为 "correct"(就像你有无限精度),结果以 limited/finite 精度的格式存储,舍入 4.00....001到4,因为四舍五入的误差小于1.11e-16,所以可以接受。