Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.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
Java 是否有可能一个精确表示为float的数字不能精确表示为double?_Java_Floating Point_Double - Fatal编程技术网

Java 是否有可能一个精确表示为float的数字不能精确表示为double?

Java 是否有可能一个精确表示为float的数字不能精确表示为double?,java,floating-point,double,Java,Floating Point,Double,我有一个问题是由另一个关于浮点数精度的问题引起的 现在,我知道浮点不能总是准确地表示,因此它们被存储为可以表示的最接近的浮点数 我的问题实际上是关于float和double表示的差异 这个问题从何而来? 假设我这样做: System.out.println(.475d+.075d); 然后输出将不是0.55,而是0.549999(在我的机器上) 然而,当我这样做时: System.out.println(.475f+.075f); 我得到了正确的答案,即0.55(对我来说有点意外) 到目前为

我有一个问题是由另一个关于浮点数精度的问题引起的

现在,我知道浮点不能总是准确地表示,因此它们被存储为可以表示的最接近的浮点数

我的问题实际上是关于
float
double
表示的差异

这个问题从何而来?

假设我这样做:

System.out.println(.475d+.075d);
然后输出将不是
0.55
,而是
0.549999
(在我的机器上)

然而,当我这样做时:

System.out.println(.475f+.075f);
我得到了正确的答案,即
0.55
(对我来说有点意外)

到目前为止,我的印象是,
double
float
更精确(double将更精确,小数位数更长)。因此,如果不能精确地表示double,那么它的等效浮点表示也将被不准确地存储

然而,我得到的结果让我有点不安。如果:

  • 我对
    精度
    的含义理解不正确
  • float
    double
    的表示方式不同,除了double有更多位这一事实之外

  • 精确只意味着更多的比特。不能表示为
    浮点
    的数字可能精确表示为
    双精度
    ,但这些情况的数量相对于可能情况的总数而言是无限小的

    对于像
    0.1
    这样的简单情况,无论可用的位数是多少,它都不能表示为固定长度的浮点数。这等于说,无论允许使用多少位数(只要位数是有限的),分数(如1/7)都不能用十进制精确表示。您可以将其近似为0.142857142857142857。。。一次又一次地重复,但无论你坚持多久,你都无法准确地写出它

    相反,如果一个数字可以精确地表示为
    浮点
    ,那么它也可以精确地表示为
    双精度
    。double具有更大的指数范围和更多的尾数位

    例如,造成明显差异的原因是,在
    float
    中,0.475与其float表示之间的差异是在“正确”的方向上,因此当发生截断时,它按照您的预期进行。当增加可用的精度时,表示“更接近”0.475,但现在位于相反的一侧。作为一个粗略的例子,假设最接近的可能浮动是0.475006,但在双精度中,最接近的可能值是0.474999。这会给你你看到的结果

    编辑:以下是一个快速实验的结果:

    public class Test {
    
        public static void main(String[] args)
        {
            float  f = 0.475f;
            double d = 0.475d;
    
            System.out.printf("%20.16f", f);
            System.out.printf("%20.16f", d);
        }
    }
    
    输出:

      0.4749999940395355  0.4750000000000000
    

    这意味着数字0.475的浮点表示形式,如果有大量的位,将比0.475小一点点。这可以在双重表示中看到。然而,第一个“错误”位出现在最右边,当被截断以适应
    浮点值时,恰好计算为0.475。这纯粹是一个意外。

    一个可以表示为
    浮点数的数字也可以表示为
    double

    您读取的只是格式化输出,而不是实际的二进制表示

    System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(.475d + .075d)));
    // 11111111100001100110011001100110011001100110011001100110011001
    System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(.475f + .075f)));
    // 111111000011001100110011001101
    
    double d = .475d + .075d;
    System.out.println(d);
    // 0.5499999999999999
    System.out.println((float)d);
    // 0.55 (as expected)
    System.out.println((double)(float)d);
    // 0.550000011920929
    
    System.out.println( .475f + .075f == 0.550000011920929d);
    // true
    

    如果人们认为浮点类型实际上代表值的范围,而不是离散值(例如,
    0.1f
    不代表13421773/134217728,而是“介于13421772.5/134217728和13421773.5/134217728之间的某个值”),则从
    double
    float
    的转换通常是准确的,而从
    float
    double
    的转换通常不会。不幸的是,Java允许隐式执行通常不准确的转换,同时要求按通常准确的方向进行类型转换

    对于
    float
    类型的每个值,都存在一个
    double
    类型的值,其范围以
    float
    范围的中心为中心。这并不意味着
    double
    是浮点值的精确表示。例如,将
    0.1f
    转换为
    double
    会产生一个值,表示“介于13421772.9999999/134217728和13421773.0000001/134217728之间的某个值”,该值与隐含公差的偏差超过一百万倍

    对于几乎所有类型的
    double
    ,都存在一个类型为
    float
    的值,其范围完全包括
    double
    所暗示的范围。唯一的例外是其范围精确集中在两个
    float
    值之间的边界上的值。将这些值转换为浮点值需要系统选择一个或另一个范围;如果当
    double
    实际表示低于其范围中心的数字时系统向上取整,或者反之亦然,
    float
    的范围不会完全包含
    double
    的范围。但实际上,这不是一个问题,因为这意味着从表示类似(13421772.5/134217728到13421773.5/134217728)的范围(13421772.499999/134217728到13421773.5000001/134217728)的
    double
    转换而来的
    float
    将表示类似(13421772.499999/134217728到13421773.5000001/134217728)的范围。与由
    float
    double
    转换产生的可怕的不精确性相比,这种微小的不精确性算不了什么

    顺便说一句,回到您使用的特定数字,当您以浮点形式进行计算时,计算结果如下:

    0.075f = 20132660±½ / 268435456 0.475f = 31876710±½ / 67108864 Sum = 18454938±½ / 33554432 0.075f=20132660±½/268435456 0.475f=31876710±½/67108864 总和=18454938±½/33554432 换句话说,总和代表一个数字