Java 每个浮点数都可以精确地表示为双精度浮点数吗?

Java 每个浮点数都可以精确地表示为双精度浮点数吗?,java,floating-point,Java,Floating Point,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) 编辑:问题的原始措辞令人困惑

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)


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

理论上,没有这样的值,所以“是的”,每个浮点都应该表示为双精度。。从浮点转换为双精度应该只需要在末尾加上四个字节的00——它们使用相同的格式存储,只是大小不同的字段。

是的,浮点是双精度的子集。浮点数和双精度浮点数的形式均为(符号*a*2^b)。浮点数和双精度浮点数之间的区别是a和b中的位数。由于double有更多的可用位,为double分配一个浮点值实际上意味着插入额外的0位。

正如每个人都说过的,“不”。但这实际上是对问题本身的一个“是”,即每个float都可以准确地表示为double。令人困惑的:)

如果我读的是正确的(其他人都在确认),那么就没有这样的值

也就是说,每个声明只保存IEEE 754标准值,因此,除了给定的内存之外,这两个声明之间的转换不应引起任何更改


(澄清:只要值足够小,可以保存在浮点数中,就不会有任何更改;显然,如果值太多而无法保存在浮点数中,则从双精度转换为浮点数将导致精度损失。)

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


<>这不使已经给出的答案无效。

< P>我把你列出的代码决定了,在C++中尝试,因为我认为它可能执行得更快一些,而且更容易做不安全的铸造。D

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

****故障****2140188725 | 1.#QNAN0--0xA00000000x7FFA1606


我将一个无符号int转换为float,然后转换为double,再转换为float。数字2140188725(0x7F90B035)产生一个NAN,并且转换为双精度和反向仍然是一个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);
}
typedef无符号整数uint;
对于(uint i=0;i<0xFFFFFFFF;++i)
{
浮点f1=*(浮点*)&i;
双d=f1;
浮动f2=(浮动)d;
如果(f1!=f2)
printf(“****故障******%u |%f--0x%08x 0x%08x\n”,i,f1,f1,f2);
如果((i%1000000)==0)
printf(“迭代:%d\n”,i);
}
@KenG:此代码:

float a = 0.1F
println "a=${a}"
double d = a
println "d=${d}"
失败不是因为无法准确表示0.1f。问题是“是否存在不能表示为double的浮点值”,这段代码没有证明。虽然不能精确地存储0.1f,但给定的值(不是精确的0.1f)可以存储为双精度(也不是精确的0.1f)。假设是Intel FPU,则a的位模式为:

0 01110110011001101

d的位模式是:

0 011111011100110011010(后面是更多的零)

具有相同的符号、指数(-4)和相同的小数部分(由上面的空格分隔)。输出中的差异是由于数字中第二个非零数字的位置(第一个是点后的1)造成的,该数字只能用双精度表示。输出字符串格式的代码将中间值存储在内存中,并且特定于浮点和双精度(即,有一个函数double to string和另一个float to string)。如果对to string函数进行了优化,以使用FPU堆栈存储to string过程的中间结果,则float和double的输出将相同,因为FPU对float和double使用相同的较大格式(80位)

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

列举所有可能的案例证明:

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);
            }
        }
    }
}
公共类TestDoubleFloat{
公共静态void main(字符串[]args){

对于(long i=Integer.MIN_VALUE;i第一个问题的答案是肯定的,“换句话说”,答案是否定的。如果您将代码中的测试更改为
If(!(f1!=f2))
第二个问题的答案变为“是”——它将为所有浮点值打印“成功”。

理论上,每个普通单曲都可以填充指数和尾数,以创建一个双精度,然后删除填充,然后返回原始单曲

当你从理论走向现实的时候,你会遇到问题。我不知道你是对理论还是实施感兴趣。如果是实施,你会很快陷入麻烦

IEEE是一种可怕的格式,我的理解是,它被故意设计成如此严格,以至于没有人能够满足它,并允许市场赶上intel(这是很久以前的事了)允许更多的竞争。如果这是真的,它失败了,无论哪种方式,我们都被困在这个可怕的规范中。像TI格式这样的东西在很多方面都远远优于现实世界。我与任何一家公司或这些格式都没有联系

由于这个规范,很少有FPU(如果有)真正满足它(在硬件上,甚至在硬件加操作系统上),以及