double/float C 中的转换

double/float conversion in C

我有这个代码

#define Third (1.0/3.0)
#define ThirdFloat (1.0f/3.0f)
int main()
{
    double a=1/3;
    double b=1.0/3.0;
    double c=1.0f/3.0f;
    printf("a = %20.15lf, b = %20.15lf, c = %20.15lf\n", a,b,c);
    float d=1/3;
    float e=1.0/3.0;
    float f=1.0f/3.0f;
    printf("d = %20.15f, e = %20.15f, f = %20.15f\n", d,e,f);

    double g=Third*3.0;
    double h=ThirdFloat*3.0;
    float i=ThirdFloat*3.0f;
    printf("(1/3)*3: g = %20.15lf; h = %20.15lf, i = %20.15f\n", g, h, i);
}

给出输出

a =    0.000000000000000, b =    0.333333333333333, c =    0.333333343267441
d =    0.000000000000000, e =    0.333333343267441, f =    0.333333343267441
(1/3)*3: g =    1.000000000000000; h =    1.000000029802322, i =    1.000000000000000

我假设 ad 的输出看起来像这样,因为编译器在除法后将整数值转换为浮点数。 b 看起来不错,e 是错误的,因为 float 精度低,因此 cf.

但我不知道为什么 g 具有正确的值(我认为 1.0/3.0 = 1.0lf/3.0lf,但 i 应该是错误的)以及为什么 h 不是与 i.

相同

但我不知道为什么 g 有正确的值(我认为 1.0/3.0 = 1.0lf/3.0lf

G 的值恰好应基于:

#define Third (1.0/3.0)    
...
double g=Third*3.0;

g=(1.0/3.0)*3.0;
1.000000000000000(当使用 "%20.15lf" 打印时)

让我们先仔细看看:使用 "%.17e"(近似十进制)和 "%a"(精确)。

#define Third (1.0/3.0)
#define ThirdFloat (1.0f/3.0f)
#define FMT "%.17e, %a"
int main(void) {
    double a=1/3;
    double b=1.0/3.0;
    double c=1.0f/3.0f;
    printf("a = " FMT "\n", a,a);
    printf("b = " FMT "\n", b,b);
    printf("c = " FMT "\n", c,c);
    puts("");
    float d=1/3;
    float e=1.0/3.0;
    float f=1.0f/3.0f;
    printf("d = " FMT "\n", d,d);
    printf("e = " FMT "\n", e,e);
    printf("f = " FMT "\n", f,f);
    puts("");
    double g=Third*3.0;
    double h=ThirdFloat*3.0;
    float i=ThirdFloat*3.0f;
    printf("g = " FMT "\n", g,g);
    printf("h = " FMT "\n", h,h);
    printf("i = " FMT "\n", i,i);
}

输出

a = 0.00000000000000000e+00, 0x0p+0
b = 3.33333333333333315e-01, 0x1.5555555555555p-2
c = 3.33333343267440796e-01, 0x1.555556p-2

d = 0.00000000000000000e+00, 0x0p+0
e = 3.33333343267440796e-01, 0x1.555556p-2
f = 3.33333343267440796e-01, 0x1.555556p-2

g = 1.00000000000000000e+00, 0x1p+0
h = 1.00000002980232239e+00, 0x1.0000008p+0
i = 1.00000000000000000e+00, 0x1p+0

But i have no idea why g has correct value

  1. (1.0/3.0)*3.0 可以在编译器或 运行 时评估为 double,并且 rounded 结果正好是 1.0 .

  2. (1.0/3.0)*3.0 可以在编译器或 运行 时使用比 double 更宽的数学计算,四舍五入的结果正好是 1.0。研究 FLT_EVAL_METHOD.

and why h isn't the same as i.

(1.0f/3.0f) 可以使用 float 数学来形成与 one-third 明显不同的 float 商:0.333333343267.... 最后的 *3.0与 1.0.

没有什么不同

输出都是正确的。我们需要看看为什么期望不对。


OP 进一步问道:“为什么 h (float * double) 不如 i (float * float) 准确?”

两者都以 0.333333343267... * 3.0 开头,而不是 one-third * 3.0
float * double 准确。两者形成一个乘积,但 float * float 是一个 float 乘积 四舍五入 到 224 中最接近的 1 部分,而更准确的 float * double 产品是 double 到 253 中最接近的 1 部分。 float * float 舍入为 1.0000000 而 float * double 舍入为 1.0000000298...

我想我找到了答案。

#define Third (1.0/3.0)
#define ThirdFloat (1.0f/3.0f)


    printf("%20.15f, %20.15lf\n", ThirdFloat*3.0, ThirdFloat*3.0);//float*double
    printf("%20.15f, %20.15lf\n", ThirdFloat*3.0f, ThirdFloat*3.0f);//float*float
    printf("%20.15f, %20.15lf\n", Third*3.0, Third*3.0);//double*double
    printf("%20.15f, %20.15lf\n\n", Third*3.0f, Third*3.0f);//float*float

    printf("%20.15f, %20.15lf\n", Third, Third);
    printf("%20.15f, %20.15lf\n", ThirdFloat, ThirdFloat);
    printf("%20.15f, %20.15lf\n", 3.0, 3.0);
    printf("%20.15f, %20.15lf\n", 3.0f, 3.0f);

并输出:

   1.000000029802322,    1.000000029802322
   1.000000000000000,    1.000000000000000
   1.000000000000000,    1.000000000000000
   1.000000000000000,    1.000000000000000

   0.333333333333333,    0.333333333333333
   0.333333343267441,    0.333333343267441
   3.000000000000000,    3.000000000000000
   3.000000000000000,    3.000000000000000

由于浮动的限制,第一行不准确。常量 ThirdFloat 的精度非常低,因此当乘以 double 时,编译器采用这个非常糟糕的近似值 (0.333333343267441),将其转换为 double 并乘以 3.0double 给出,这也给出了错误的结果 (1.000000029802322)。

但是如果 ThirdFloat,也就是 float,乘以 3.0f,也就是 float,编译器可以通过取 float 的精确值来避免近似=23=] 并将其乘以 3,这就是我得到准确结果的原因。