编程语言
首页 > 编程语言> > c# – 为什么解析的double不等于初始化的double,假设它具有相同的值?

c# – 为什么解析的double不等于初始化的double,假设它具有相同的值?

作者:互联网

当我执行这一行时:

double dParsed = double.Parse("0.00000002036");

dParsed实际获取值:0.000000020360000000000002

与此系列相比,

double dInitialized = 0.00000002036;

在这种情况下,dInitialized的值正好是0.00000002036

他们在调试器中:

这种不一致是一件令人烦恼的事情,因为我想按照以下方式运行测试:

[Subject("parsing doubles")]
public class when_parsing_crazy_doubles
    {
    static double dInitialized = 0.00000002036;
    static double dParsed;
    Because of = () => dParsed = double.Parse("0.00000002036");
    It should_match = () =>  dParsed.ShouldBeLike(dInitialized);
    }

这当然失败了:

Machine.Specifications.SpecificationException
"":
  Expected: [2.036E-08]
  But was:  [2.036E-08]

在我的生产代码中,从数据文件中读取“已解析”的双精度数,而将比较值硬编码为对象初始值设定项.超过数百条记录,其中4或5条不匹配.原始数据显示在文本文件中,如下所示:

0.00000002036 0.90908165072 6256.77753019160

所以被解析的值只有11位小数.解决这种不一致的任何想法?

虽然我接受比较双精度的等式是有风险的,但我很惊讶编译器可以在文本用作对象初始化器时得到精确的表示,但是当解析完全相同时,double.Parse无法得到精确的表示.文本.如何将解析后的双精度数限制为11位小数?

解决方法:

Compared to this line,

double dInitialized = 0.00000002036;

in which case the value of dInitialized is exactly 0.00000002036

如果您有任何远离商用计算机的东西,则dInitialized不会初始化为0.00000002036.它不能是因为基数10 0.00000002036在基数2中没有有限的表示.

你的错误是期望两个双打相等.这通常不是一个好主意.除非你有充分的理由并知道自己在做什么,否则最好不要将两个双打比作平等或不平等.而是测试两者之间的差异是否在零的小ε之内.

正确地获得该epsilon的大小有点棘手.如果你的两个数字都很小(例如,少于一个),则1e-15的epsilon可能是合适的.如果数字很大(例如,大于10),那么小的epsilon值就相当于测试相等性.

编辑:我没有回答这个问题.

How can I limit the parsed doubles to 11 decimal places?

如果您不必担心非常小的值,

static double epsilon = 1e-11;
if (Math.Abs(dParsed-dInitialized) > epsilon*Math.Abs(dInitialized)) {
    noteTestAsFailed();
}

您应该能够安全地将该epsilon更改为4e-16.

编辑#2:为什么编译器和double.Parse为同一文本产生不同的内部表示?

那有点明显,不是吗?编译器和double.Parse使用不同的算法.有问题的数字0.00000002036非常接近于是否应该使用向上舍入或向下舍入来产生可表示的值,该值在所需值的一半ULP(0.00000002036)内. “正确”值是在期望值的ULP的一半内的值.在这种情况下,编译器做出正确的选择舍入值的决定,而解析器做出错误的选择舍入值的决定.

值0.00000002036是一个讨厌的角落案例.它不是一个可以代表的值.可以精确表示为IEEE双精度的两个最接近的值是6153432421838462/2 ^ 78和6153432421838463/2 ^ 78.这两者之间的值是12306864843676925/2 ^ 79,这非常非常接近0.00000002036.这就是为什么这是一个极端的案例.我怀疑你找到的所有值,其中编译的值不等于double.Parse的值是极端情况,其中所需的值几乎在两个最接近的可精确表示的值之间.

编辑#3:

以下是许多不同的方法来解释0.00000002036:

> 2 / 1e8 3 / 1e10 6 / 1e11
> 2 * 1e-8 3 * 1e-10 6 * 1e-11
> 2.036 * 1e-8
> 2.036 / 1e8
> 2036 * 1e-11
> 2036 / 1e11

在理想的计算机上,所有这些都是相同的.不要指望在使用有限精度算术的计算机上就是这种情况.

标签:c,double,math,mspec
来源: https://codeday.me/bug/20190624/1280270.html