您不会失去精确度;您正在从不太精确的表示(浮点,32 位长)向上转换为更精确的表示(双精度,64 位长)。你得到的更精确的表示(超过某个点)只是垃圾。如果你将它从一个双精度浮点数转换回浮点数,你将拥有与以前完全相同的精度。
这里发生的情况是您为浮点数分配了 32 位。然后,您向上转换为双精度数,再添加 32 位来表示您的数字(总共 64 位)。这些新位是最不重要的(小数点右边最远的),并且与实际值无关,因为它们以前是不确定的。结果,这些新位具有您进行向上转换时碰巧具有的任何值。它们和以前一样不确定——换句话说,垃圾。
当您从 double 向下转换为 float 时,它会去掉那些最低有效位,留下 0.300000(7 位精度)。
从字符串转换为浮点数的机制不同;编译器需要分析字符串'0.3f'的语义并找出它与浮点值的关系。它不能像浮点/双精度转换那样通过位移来完成——因此,这是您期望的值。
有关浮点数如何工作的更多信息,您可能有兴趣查看有关 IEEE 754-1985 标准的 this 维基百科文章(其中包含一些方便的图片和对事物机制的良好解释),以及 @ 987654322@wiki文章2008年标准更新。
编辑:
首先,正如@phoog 在下面指出的那样,从浮点数向上转换为双精度数并不像在为记录数字而保留的空间中添加另外 32 位那么简单。实际上,您将为指数增加 3 位(总共 11 位),并为分数增加 29 位(总共 52 位)。加上符号位,你总共得到了 64 位作为双精度位。
此外,暗示在那些最不重要的位置存在“垃圾位”是一个粗略的概括,并且可能不适用于 C#。一些解释和下面的一些测试向我表明,这对于 C#/.NET 是确定性的,并且可能是转换中某些特定机制的结果,而不是保留内存以提高精度。
回到以前,当您的代码编译成机器语言二进制文件时,编译器(至少是 C 和 C++ 编译器)不会在您保留时添加任何 CPU 指令来“清除”或初始化内存中的值变量的空间。因此,除非程序员将变量显式初始化为某个值,否则为该位置保留的位的值将保持它们在您保留该内存之前的任何值。
在 .NET 领域,您的 C# 或其他 .NET 语言编译成中间语言(CIL,通用中间语言),然后由 CLR 即时编译以作为本机代码执行。 C# 编译器或 JIT 编译器可能会或可能不会添加变量初始化步骤;我不知道。
以下是我所知道的:
- 我通过将浮点数转换为三个不同的双精度来测试这一点。每个结果都具有完全相同的值。
- 该值与上面@rerun 的值完全相同:
double d1 = System.Convert.ToDouble(f); 结果:d1 : 0.300000011920929
- 如果我使用
double d2 = (double)f; 进行投射,我会得到相同的结果结果:d2 : 0.300000011920929
我们三个人获得相同的值,看起来向上转换的值是确定性的(实际上不是垃圾位),表明 .NET 在我们所有的机器上都以相同的方式做某事 .仍然可以说,附加数字的精确度不会比以前高或低,因为 0.3f 并不完全等于 0.3——它等于 0.3,精度高达 7 位。我们对前七位以外的其他数字的值一无所知。