每个浮点数都可以完全表示为双精度数吗?

float变量的每个可能值都可以精确地表示在一个double变量中吗?

换句话说,对于所有可能的值, X将成功:

 float f1 = X; double d = f1; float f2 = (float)d; if(f1 == f2) System.out.println("Success!"); else System.out.println("Failure!"); 

我怀疑是没有例外,或者只有边缘情况(如+/-无穷大或NaN)。

编辑 :问题的原始措辞令人困惑(说明两种方式,一种方式将回答“否”,另一种方式将回答“是”同一答案)。 我已经对它进行了重写,以便与问题标题相匹配。

是。

通过列举所有可能的案例certificate:

 public class TestDoubleFloat { public static void main(String[] args) { for (long i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i++) { float f1 = Float.intBitsToFloat((int) i); double d = (double) f1; float f2 = (float) d; if (f1 != f2) { if (Float.isNaN(f1) && Float.isNaN(f2)) { continue; // ok, NaN } fail("oops: " + f1 + " != " + f2); } } } } 

在我的机器上完成12秒钟。 32位很小

从理论上讲,没有这样的值,所以“是”,每个浮点数应该可以表示为double。从float转换为double应该只涉及在末尾添加四个字节0​​0 – 它们是使用相同的格式,只是使用不同大小的字段。

是的,花车是双打的子集。 浮动和双打都有forms(符号* a * 2 ^ b)。 浮点数和双精度数之间的差异是a和b中的位数。 由于双精度具有更多可用位,因此有效地将浮点值赋值给双精度意味着插入额外的0位。

正如大家已经说过的那样,“不”。 但这对问题本身来说实际上是“是”,即每个浮点数可以精确地表示为双精度。 混乱。 🙂

如果我正确地阅读语言规范 (并且正如其他人所确认的那样),那么就没有这样的价值。

也就是说,每个声称只保留IEEE 754标准值,因此两者之间的转换应该不会发生变化,除非在给定的内存中。

(澄清:只要该值足够小以保持在浮点数中就不会有变化;显然如果值太多而无法在浮点数中保持开头,则从double转换为float将导致失去精确度。)

@KenG:这段代码:

 float a = 0.1F println "a=${a}" double d = a println "d=${d}" 

失败不是因为0.1f无法准确表示。 问题是“是否存在一个不能表示为double的浮点值”,此代码无法certificate。 尽管0.1f不能精确存储,但给定a的值(精确度不是0.1f)可以存储为double(也不会精确地为0.1f)。 假设是英特尔FPU,a的位模式是:

0 01111011 10011001100110011001101

并且d的位模式是:

0 01111111011 100110011001100110011010(后面有更多的零)

它具有相同的符号,指数(在两种情况下为-4)和相同的小数部分(由上面的空格分隔)。 输出的差异是由于数字中第二个非零数字的位置(第一个是点之后的1),它只能用双精度表示。 输出字符串格式的代码在内存中存储中间值,并且特定于浮点数和双精度数(即,有一个函数double-to-string和另一个float-to-string)。 如果to-string函数被优化为使用FPU堆栈来存储to-string进程的中间结果,则float和double的输出相同,因为FPU对于float都使用相同的,更大的格式(80bits)并加倍。

没有浮点值不能以相同的方式存储在double中,即浮点值集是double值集的子集。

Snark: NaN将在转换之后(或之前)进行不同的比较。

但是,这并没有使已经给出的答案无效。

我拿了你列出的代码并决定在C ++中尝试它,因为我认为它可能会执行得更快一些,并且更容易进行不安全的转换。 😀

我发现对于有效数字,转换有效,并且在转换后得到精确的按位表示。 但是,对于非数字,例如1.#QNAN0等,结果将使用非数字的简化表示而不是源的精确位。 例如:

****失败**** 2140188725 | 1.#QNAN0 – 0xa0000000 0x7ffa1606

我将一个unsigned int转换为float,然后转换为double并返回float。 数字2140188725(0x7F90B035)导致NAN并且转换为double并且返回仍然是NAN但不完全相同的 NAN。

这是简单的C ++代码:

 typedef unsigned int uint; for (uint i = 0; i < 0xFFFFFFFF; ++i) { float f1 = *(float *)&i; double d = f1; float f2 = (float)d; if(f1 != f2) printf("**** FAILURE **** %u | %f -- 0x%08x 0x%08x\n", i, f1, f1, f2); if ((i % 1000000) == 0) printf("Iteration: %d\n", i); } 

第一个问题的答案是肯定的,但“换句话说”的答案却是否定的。 如果您将代码中的测试更改为if (!(f1 != f2)) ,则第二个问题的答案变为是 – 它将为所有浮点值打印“成功”。

理论上,每个普通单个都可以使用指数和尾数填充以创建一个double,然后删除填充,然后返回到原始单个。

当你从理论走向现实时,你会遇到问题。 我不知道你是否对理论或实施感兴趣。 如果它是实施,那么你可以迅速陷入困境。

IEEE是一种可怕的格式,我的理解是它故意设计得如此强硬以至于没有人能够满足它并允许市场赶上英特尔(这是一段时间之后)允许更多的竞争。 如果这是真的失败了,无论哪种方式我们都坚持这个可怕的规范。 像TI这样的东西在很多方面都远远优于现实世界。 我与公司或任何这些格式没有关联。

由于这个规范,实际上满足它的fpus很少(在硬件中,甚至在硬件中加上操作系统),而那些经常在下一代中失败的fpus。 (谷歌:TestFloat)。 这些天的问题往往在于浮动和浮动到int的int,而不是像上面指定的那样单个加倍和加倍。 当然fpu要执行哪些操作才能进行转换? 加0? 乘以1? 取决于fpu和编译器。

与上述问题相关的IEEE问题是,数字的方式不止一种,不是每个数字都可以表示,而是可以表示多个数字。 如果我想打破你的代码,我会从零开始,希望两个操作之一将它转换为加零。 然后我会尝试非正规。 并且它应该通过信令nan失败,但是您将其称为已知exception。

问题是等号,这里是关于浮点的规则第一,从不使用等号。 等于是比较而不是值比较,如果您有两个以不同方式表示的值(例如,加零和零),即使数字相同,位比较也会失败。 大于和小于在fpu中完成,equals用整数alu完成。

我意识到您可能使用等于解释问题,而不一定是您想要成功或失败的代码。

如果浮点类型被视为表示精确值,那么正如其他海报所指出的那样,每个float值都可以表示为double ,但只有几个double值可以用float表示。 另一方面,如果人们认识到浮点值是近似值,那么人们就会意识到真实情况是相反的。 如果使用非常精确的仪器测量3.437mm的东西,可以正确描述尺寸为3.4mm。 如果使用标尺测量物体为3.4mm,则将其尺寸描述为3.400mm是不正确的。

在该范围的顶部存在更大的问题。 有一个float值表示:“计算值超过2 ^ 127一个未知数量”,但是没有double值表示这样的事情。 将“无穷大”从单个变为双精度将产生“计算值超过2 ^ 1023未知量”的值,该值超过googol。