Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将float转换为double会丢失精度,但不会通过ToString_C#_String_Floating Point_Double_Precision - Fatal编程技术网

C# 将float转换为double会丢失精度,但不会通过ToString

C# 将float转换为double会丢失精度,但不会通过ToString,c#,string,floating-point,double,precision,C#,String,Floating Point,Double,Precision,我有以下代码: float f = 0.3f; double d1 = System.Convert.ToDouble(f); double d2 = System.Convert.ToDouble(f.ToString()); 结果相当于: d1 = 0.30000001192092896; d2 = 0.3; 我很想知道这是为什么?这不是精度的损失。3不是。当系统转换为它所舍入的字符串时;如果你打印出足够的有效数字,你会得到更有意义的东西 看得更清楚 float f = 0.3f; do

我有以下代码:

float f = 0.3f;
double d1 = System.Convert.ToDouble(f);
double d2 = System.Convert.ToDouble(f.ToString());
结果相当于:

d1 = 0.30000001192092896;
d2 = 0.3;

我很想知道这是为什么?

这不是精度的损失。3不是。当系统转换为它所舍入的字符串时;如果你打印出足够的有效数字,你会得到更有意义的东西

看得更清楚

float f = 0.3f;
double d1 = System.Convert.ToDouble(f);
double d2 = System.Convert.ToDouble(f.ToString("G20"));

string s = string.Format("d1 : {0} ; d2 : {1} ", d1, d2);
输出

"d1 : 0.300000011920929 ; d2 : 0.300000012 "

你没有失去精确性;您正在从精度较低的表示(浮点,32位长)向上转换为更精确的表示(双精度,64位长)。你在更精确的表达中得到的(超过某一点)只是垃圾。如果你把它从一个双精度抛回一个浮点数,你会有和你以前一样的精度

这里发生的是,您已经为您的浮点分配了32位。然后向上转换为double,再添加32位来表示数字(总共64位)。这些新位是最低有效位(小数点右侧最远的位),与实际值无关,因为它们以前是不确定的。因此,这些新的比特值与您进行上推时的值相同。它们和以前一样不确定——换句话说,是垃圾

当您从双精度向下转换为浮点时,它将删除那些最低有效位,留下0.300000(7位精度)

从字符串转换为浮点的机制是不同的;编译器需要分析字符串“0.3f”的语义,并找出它与浮点值的关系。它不能像浮点/双精度转换那样使用位移位——因此,就是您期望的值

有关浮点数如何工作的更多信息,您可能有兴趣查看有关IEEE 754-1985标准的维基百科文章(其中有一些方便的图片和对事物机制的良好解释),以及关于2008年标准更新的维基文章

编辑:

首先,正如@phoog在下面指出的,从一个浮点数向上转换为一个双精度浮点数并不像在保留的空间中再添加一个32位来记录数字那么简单。实际上,指数加3位(总共11位),分数加29位(总共52位)。加上符号位,你就得到了双精度的64位

此外,建议在这些最不重要的位置存在“垃圾位”是一个总体概括,对于C#来说可能不正确。一点解释,以及下面的一些测试向我表明,这对于C#/.NET来说是确定的,可能是转换过程中某些特定机制的结果,而不是为了额外的精度而保留内存

<>以前,当你的代码编译成机器语言二进制时,编译器(C和C++编译器至少)不会在你为变量保留空间的时候,添加任何CPU指令来“清除”或初始化内存中的值。因此,除非程序员显式地将变量初始化为某个值,否则为该位置保留的位的值将保持在您保留该内存之前的任何值

在.NETLand中,您的C#或其他.NET语言编译成一种中间语言(CIL,公共中间语言),然后由CLR及时编译以作为本机代码执行。C#编译器或JIT编译器可能会添加变量初始化步骤,也可能不会添加;我不确定

以下是我所知道的:

  • 我通过将浮子投到三个不同的双打来测试这一点。每个结果都有完全相同的值
  • 该值与上面@rerun的值完全相同:
    double d1=System.Convert.ToDouble(f)结果:
    d1:0.3000001920929
  • 如果我使用
    double d2=(double)f进行转换,我会得到相同的结果结果:
    d2:0.3000001120929

当我们三个人得到相同的值时,向上转换的值看起来是确定的(而不是垃圾位),这表明.NET在我们所有的机器上都以相同的方式进行操作。仍然可以说,附加数字的精度与以前一样,因为0.3f并不完全等于0.3——它等于0.3,精度高达7位。除了前七位之外,我们对其他数字的值一无所知

在本例和其他相同的情况下,我使用十进制转换来获得正确的结果

float ff = 99.95f;
double dd = (double)(decimal)ff;

你可能会发现这很有用。啊哈,这很有意义,所以默认的ToString方法只是简单地截断输出,取整(从技术上讲,这会降低精度)。但是舍入允许我检索我设置的初始值。+1!两个问题。。。转换为字符串时,浮点值四舍五入为多少位?更重要的是,为什么?如果有人使用浮点数并指定了一个值,但由于浮点数的限制,无法存储该精确值,那么ToString究竟为什么会决定为您取整?更糟糕的是,调试器输出当然也会执行相同的操作,因此(float)0.3之类的内容仍然会在调试输出中显示0.3,并且您永远不会意识到您正在失去该精度。这太愚蠢了。这就是所有浮点运算的方式。只有这么多的位来表示天生的许多实数。这里有一个误差ε,显示逻辑知道,当ε3足够低时,它会显示。具体细节在链接中谢谢Joe,这里有一些很棒的信息,我了解第一行中的浮点与双精度转换,主要问题是了解第二行中发生了什么,以实现我所期望的结果。谢谢关于最低有效位的那个位是任何可能的垃圾