首页 文章

float和double有什么区别?

提问于
浏览
341

我已经读过双精度和单精度之间的区别 . 但是,在大多数情况下, floatdouble 似乎是可以互换的,即使用一个或另一个似乎不会影响结果 . 这是真的吗?浮动和双打什么时候可以互换?它们之间有什么区别?

11 回答

  • 2

    巨大的差异 .

    顾名思义,double的精度为float [1]的2倍 . 通常, double 具有15个十进制数字的精度,而 float 具有7个 .

    以下是计算位数的方式:

    double有52个尾数位1个隐藏位:log(253)÷log(10)= 15.95个数字float有23个尾数位1个隐藏位:log(224)÷log(10)= 7.22个数字

    这种精确度损失可能导致截断错误更容易浮动,例如,

    float a = 1.f / 81;
    float b = 0;
    for (int i = 0; i < 729; ++ i)
        b += a;
    printf("%.7g\n", b); // prints 9.000023
    

    double a = 1.0 / 81;
    double b = 0;
    for (int i = 0; i < 729; ++ i)
        b += a;
    printf("%.15g\n", b); // prints 8.99999999999996
    

    此外,float的最大值约为 3e38 ,但是double约为 1.7e308 ,因此使用 float 可以比 double 更轻松地命中"infinity"(即特殊的浮点数),例如:计算60的阶乘 .

    在测试期间,可能有一些测试用例包含这些巨大的数字,如果使用浮点数,可能会导致程序失败 .


    当然,有时甚至 double 都不够准确,因此我们有时会 long double [1](上面的例子在Mac上给出9.000000000000000066),但是所有浮点类型都会出现舍入错误,所以如果精度非常重要(例如,货币处理)你应该使用 int 或分数类 .


    此外,不要使用 += 来汇总大量浮点数,因为错误会很快累积 . 如果您使用的是Python,请使用 fsum . 否则,尝试实现Kahan summation algorithm .


    [1]:C和C标准没有指定float,double和long double的表示 . 这三个都可以实现为IEEE双精度 . 然而,对于大多数体系结构(gcc,MSVC; x86,x64,ARM),float实际上是IEEE单精度浮点数(binary32),而double是IEEE双精度浮点数(binary64) .

  • 51

    以下是标准C99(ISO-IEC 98996.2.5§10)或C 2003(ISO-IEC 14882-20033.1.9§8)标准所说的:

    有三种浮点类型:float,double和long double . double类型提供至少与float一样多的精度,long double类型提供至少与double一样多的精度 . float类型的值集是double类型的值集的子集; double类型的值集是long double类型的值集的子集 .

    C标准增加:

    浮点类型的值表示是实现定义的 .

    我建议看一下涵盖IEEE浮点标准的优秀What Every Computer Scientist Should Know About Floating-Point Arithmetic . 你'll learn about the representation details and you'll会意识到在幅度和精度之间存在权衡 . 随着幅度减小,浮点表示的精度增加,因此-1和1之间的浮点数是具有最高精度的浮点数 .

  • 25

    给定二次方程:x2 - 4.0000000 x 3.9999999 = 0,精确根到10位有效数字,r1 = 2.000316228,r2 = 1.999683772 .

    使用 floatdouble ,我们可以编写一个测试程序:

    #include <stdio.h>
    #include <math.h>
    
    void dbl_solve(double a, double b, double c)
    {
        double d = b*b - 4.0*a*c;
        double sd = sqrt(d);
        double r1 = (-b + sd) / (2.0*a);
        double r2 = (-b - sd) / (2.0*a);
        printf("%.5f\t%.5f\n", r1, r2);
    }
    
    void flt_solve(float a, float b, float c)
    {
        float d = b*b - 4.0f*a*c;
        float sd = sqrtf(d);
        float r1 = (-b + sd) / (2.0f*a);
        float r2 = (-b - sd) / (2.0f*a);
        printf("%.5f\t%.5f\n", r1, r2);
    }   
    
    int main(void)
    {
        float fa = 1.0f;
        float fb = -4.0000000f;
        float fc = 3.9999999f;
        double da = 1.0;
        double db = -4.0000000;
        double dc = 3.9999999;
        flt_solve(fa, fb, fc);
        dbl_solve(da, db, dc);
        return 0;
    }
    

    运行程序给了我:

    2.00000 2.00000
    2.00032 1.99968
    

    请注意,数字不是很大,但仍然可以使用 float 获得取消效果 .

    (事实上,上述不是使用单精度或双精度浮点数求解二次方程的最佳方法,但即使使用more stable method,答案也保持不变 . )

  • 8
    • double是64,单精度(float)是32位 .

    • 双尾有一个更大的尾数(实数的整数位) .

    • 双倍的任何不准确性都会变小 .

  • 3

    浮点计算中涉及的数字大小并不是最相关的 . 这是正在执行的相关计算 .

    本质上,如果您正在执行计算并且结果是无理数或重复的小数,那么当该数被压缩到您正在使用的有限大小数据结构中时,将存在舍入误差 . 由于double是float的两倍,因此舍入误差会小很多 .

    测试可能会专门使用会导致此类错误的数字,因此测试您在代码中使用了适当的类型 .

  • 18

    浮点数的精度低于双精度数 . 虽然您已经知道,但请阅读What WE Should Know About Floating-Point Arithmetic以便更好地理解 .

  • 417

    类型为float,长32位,精度为7位 . 虽然它可以存储非常大或非常小范围的值(/ - 3.4 * 10 ^ 38或* 10 ^ -38),但它只有7位有效数字 .

    类型为double,64位长,具有更大的范围(* 10 ^ / -308)和15位精度 .

    类型long double是名义上为80位,但给定的编译器/ OS配对可以将其存储为12-16个字节以用于对齐目的 . long double有一个非常庞大的指数,应该有19位数的精度 . 微软凭借其无限的智慧,将长双倍限制为8字节,与普通双重相同 .

    一般来说,只需在需要浮点值/变量时使用double类型 . 默认情况下,表达式中使用的文字浮点值将被视为双精度值,并且返回浮点值的大多数数学函数都会返回双精度值 . 如果你只使用双倍,你会省去许多头痛和类型 .

  • 11

    我刚刚遇到一个错误,让我永远弄明白,并且可能会给你一个漂浮精度的好例子 .

    #include <iostream>
    #include <iomanip>
    
    int main(){
      for(float t=0;t<1;t+=0.01){
         std::cout << std::fixed << std::setprecision(6) << t << std::endl;
      }
    }
    

    输出是

    0.000000
    0.010000
    0.020000
    0.030000
    0.040000
    0.050000
    0.060000
    0.070000
    0.080000
    0.090000
    0.100000
    0.110000
    0.120000
    0.130000
    0.140000
    0.150000
    0.160000
    0.170000
    0.180000
    0.190000
    0.200000
    0.210000
    0.220000
    0.230000
    0.240000
    0.250000
    0.260000
    0.270000
    0.280000
    0.290000
    0.300000
    0.310000
    0.320000
    0.330000
    0.340000
    0.350000
    0.360000
    0.370000
    0.380000
    0.390000
    0.400000
    0.410000
    0.420000
    0.430000
    0.440000
    0.450000
    0.460000
    0.470000
    0.480000
    0.490000
    0.500000
    0.510000
    0.520000
    0.530000
    0.540000
    0.550000
    0.560000
    0.570000
    0.580000
    0.590000
    0.600000
    0.610000
    0.620000
    0.630000
    0.640000
    0.650000
    0.660000
    0.670000
    0.680000
    0.690000
    0.700000
    0.710000
    0.720000
    0.730000
    0.740000
    0.750000
    0.760000
    0.770000
    0.780000
    0.790000
    0.800000
    0.810000
    0.820000
    0.830000
    0.839999
    0.849999
    0.859999
    0.869999
    0.879999
    0.889999
    0.899999
    0.909999
    0.919999
    0.929999
    0.939999
    0.949999
    0.959999
    0.969999
    0.979999
    0.989999
    0.999999
    

    正如您在0.83之后看到的那样,精度显着下降 .

    但是,如果我将 t 设置为double,则不会发生此类问题 .

    我花了五个小时才意识到这个小错误,这毁了我的程序 .

  • 7

    使用浮点数时,您不能相信您的本地测试将与服务器端的测试完全相同 . 环境和编译器可能与您本地系统和运行最终测试的位置不同 . 我之前在一些TopCoder比赛中已经多次看到这个问题,特别是如果你试图比较两个浮点数 .

  • 8

    内置比较操作的不同之处在于,当您将2个数字与浮点数进行比较时,数据类型的差异(即浮点数或双精度数)可能会导致不同的结果 .

  • -1

    int (整数)不同, float 具有小数点, double 也是如此 . 但两者之间的区别在于 double 的详细程度是 float 的两倍,这意味着它可以使小数点后的数字加倍 .

相关问题