Ошибка разделения времени компиляции GCC - PullRequest
2 голосов
/ 03 июня 2010

Может кто-нибудь объяснить это поведение?

test.c:

#include <stdio.h>

int main(void)
{
    printf("%d, %d\n", (int) (300.6000/0.05000), (int) (300.65000/0.05000));
    printf("%f, %f\n", (300.6000/0.05000), (300.65000/0.05000));
    return 0;
}

$ gcc test.c

$ ./a.out
6012, 6012
6012.000000, 6013.000000

Я проверил ассемблерный код, и он устанавливает оба аргумента первого printf как 6012, так что, похоже, это ошибка времени компиляции.

Ответы [ 4 ]

9 голосов
/ 03 июня 2010

Пробег

#include <stdio.h>

int main(void)
{
    printf("%d, %d\n", (int) (300.6000/0.05000), (int) (300.65000/0.05000));
    printf("%.20f %.20f\n", (300.6000/0.05000), (300.65000/0.05000));
    return 0;
}

и это должно быть более понятно. Значение второго (после точечного деления с плавающей запятой, которое не является точным) составляет ~ 6012.9999999999991, поэтому, когда вы усекаете его с помощью (int), gcc достаточно умен, чтобы вставить 6012 во время компиляции.

Когда вы печатаете плавающие числа, printf по умолчанию форматирует их для отображения только с точностью до 6 цифр, что означает, что вторая печатается как 6013.000000.

7 голосов
/ 03 июня 2010

printf() округляет числа с плавающей запятой при их печати. Если вы добавите больше точности, вы сможете увидеть, что происходит:

$ cat gccfloat.c
#include <stdio.h>

int main(void)
{
    printf("%d, %d\n", (int) (300.6000/0.05000), (int) (300.65000/0.05000));
    printf("%.15f, %.15f\n", (300.6000/0.05000), (300.65000/0.05000));
    return 0;
}

$ ./gccfloat
6012, 6012
6012.000000000000000, 6012.999999999999091
1 голос
/ 03 июня 2010

Звучит как ошибка округления. 300.65000/0.05000 рассчитывается (с плавающей запятой) как что-то вроде 6012.99999999. При приведении к типу int он усекается до 6012. Конечно, все это предварительно рассчитывается при оптимизации компилятора, поэтому конечный двоичный файл просто содержит значение 6012, которое вы видите.

Причина, по которой вы не видите того же во втором утверждении, заключается в том, что он округлен для отображения на printf, а не усечен , как это происходит, когда приведение к int. (См. Ответ Джона Кугельмана.)

0 голосов
/ 30 марта 2018

Это очевидно, но никто не сказал этого: результат обоих делений - ЕСТЕСТВЕННЫЕ ЧИСЛА. Значит компилятор где-то не так? Может быть. И я воспроизвел ту же проблему и в MS VS 2015, так что похоже, что неправильные компиляторы равны 2. И я держу пари, что копейка, что и LLVM делает то же самое. Так может быть, компиляторы правы? Отвечая на это не для меня, я просто заметил следующее: похоже, что это проблема неявного приведения из "деления с литералами с десятичными числами" на удвоение.

Следующий небольшой тест показывает, что содержимое двойного числа, присваиваемого из результата времени компиляции деления 2 литералов без приведения, неверно. Если результат ранее был приведен к плавающему, результат становится правильным.

#include <stdio.h>

int main(void)
{
    float fb = (300.65000 / 0.05000);
    float fa = (300.6000 / 0.05000);
    float fb1 = 6013.0;
    float fa1 = 6012.0;
    char bufffa[sizeof(float) * 2 +1] = { 0 };
    char bufffb[sizeof(float) * 2 +1] = { 0 };

    double db = (300.65000 / 0.05000);
    double da = (300.6000 / 0.05000);
    double db1 = 6013.0;
    double da1 = 6012.0;
    char buffda[sizeof(double) * 2 + 1] = { 0 };
    char buffdb[sizeof(double) * 2 + 1] = { 0 };
    int i;
    printf("sizeof(float)=%d, sizeof(double)=%d\n", sizeof(float), sizeof(double));
    for (i = 0; i < sizeof(fa); i++)
    {
        sprintf(bufffa + (i * 2), "%02X", ((unsigned char*)(&fa))[i]);
        sprintf(bufffb + (i * 2), "%02X", ((unsigned char*)(&fb))[i]);
    }
    printf("OK: float binary form (compile time division):     fa=%s, fb=%s\n", bufffa, bufffb);
    for (i = 0; i < sizeof(fa1); i++)
    {
        sprintf(bufffa + (i * 2), "%02X", ((unsigned char*)(&fa1))[i]);
        sprintf(bufffb + (i * 2), "%02X", ((unsigned char*)(&fb1))[i]);
    }
    printf("OK: float binary form (NOT compile time division): fa1=%s, fb1=%s\n", bufffa, bufffb);

    for (i = 0; i < sizeof(da); i++)
    {
        sprintf(buffda + (i * 2), "%02X", ((unsigned char*)(&da))[i]);
        sprintf(buffdb + (i * 2), "%02X", ((unsigned char*)(&db))[i]);
    }
    printf("NOT OK: double binary form (compile time division):    da=%s, db=%s\n", buffda, buffdb);

    for (i = 0; i < sizeof(da1); i++)
    {
        sprintf(buffda + (i * 2), "%02X", ((unsigned char*)(&da1))[i]);
        sprintf(buffdb + (i * 2), "%02X", ((unsigned char*)(&db1))[i]);
    }
    printf("OK: double binary form (NOT compile time division):da1=%s, db1=%s\n", buffda, buffdb);


    printf("incorrect values:\n");
    printf("printf(int) compile time division, literal: a=%d, b=%d\n", (int)(300.6000 / 0.05000), (int)(300.65000 / 0.05000));
    printf("printf(float) compile time division, literal: a=%.15f, b=%.15f\n", (300.6000 / 0.05000), (300.65000 / 0.05000));

    printf("printf(double) compile time division: da=%.15f, db=%.15f\n", da, db);

    printf("correct values:\n");
    printf("printf(int) compile time division, literal with cast: a=%d, b=%d\n", (int)(300.6000 / 0.05000), (int)(float)(300.65000 / 0.05000));
    printf("printf(float) compile time division, literal  with cast: a=%.15f, b=%.15f\n", (float)(300.6000 / 0.05000), (float)(300.65000 / 0.05000));
    printf("printf(float) compile time division: fa=%.15f, fb=%.15f\n", fa, fb);
    printf("printf(float) NOT compile time division: fa1=%.15f, fb1=%.15f\n", fa1, fb1);
    printf("printf(double) NOT compile time division: da1=%.15f, db1=%.15f\n", da1, db1);



    return 0;
}

это выводит следующее в MS-VS.2015:

sizeof(float)=4, sizeof(double)=8
OK: float binary form (compile time division):     fa=00E0BB45, fb=00E8BB45
OK: float binary form (NOT compile time division): fa1=00E0BB45, fb1=00E8BB45
NOT OK: double binary form (compile time division):    da=00000000007CB740, db=FFFFFFFFFF7CB740
OK: double binary form (NOT compile time division):da1=00000000007CB740, db1=00000000007DB740
incorrect values:
printf(int) compile time division, literal: a=6012, b=6012
printf(float) compile time division, literal: a=6012.000000000000000, b=6012.999999999999091
printf(double) compile time division: da=6012.000000000000000, db=6012.999999999999091
correct values:
printf(int) compile time division, literal with cast: a=6012, b=6013
printf(float) compile time division, literal  with cast: a=6012.000000000000000, b=6013.000000000000000
printf(float) compile time division: fa=6012.000000000000000, fb=6013.000000000000000
printf(float) NOT compile time division: fa1=6012.000000000000000, fb1=6013.000000000000000
printf(double) NOT compile time division: da1=6012.000000000000000, db1=6013.000000000000000
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...