Создание рационального числа из числа с плавающей точкой дает неточные результаты - PullRequest
1 голос
/ 30 марта 2019

Я тренирую свои навыки Си.Я выполнял функцию, которая получает число с плавающей точкой и должна была возвращать дробь, которую я сделал со структурой.Когда я пытался набрать номер, я начал видеть странные результаты.Я подозреваю, что это какая-то ошибка в функции floor ().

Я использую Code :: Blocks, и я прочитал, что функция pow () выдает некоторую ошибку, используя ее, поэтому я подумал, что она должнабудь то.

Я много читаю о том, как работает функция floor (), но я не мог понять, как это исправить и предотвратить повторное получение неправильных чисел.

Моя функция:

Fraction ftof (float f)
/* Transforms a float in a struct type fraction. */
{
    Fraction frac;
    int i, decimalPlaces; 
    float decimalPart, numerator;

    decimalPlaces = 0;
    printf("Number of decimal places: %d\n", decimalPlaces);

    decimalPart = f - floor(f);
    printf("Decimal part: %f\n\n", decimalPart);

    while (decimalPart != 0)
    {
        decimalPlaces++;

        printf("Houses number updated: %d\n", decimalPlaces);

        decimalPart = decimalPart * 10;

        decimalPart = decimalPart - floor(decimalPart);

        printf("Decimal part updated: %f\n", decimalPart);
    }

    numerator = f;
    frac.denominator = 1;

    for (i = 0; i < decimalPlaces; i++)
    {
        numerator = numerator * 10;

        frac.denominator = frac.denominator * 10;
    }

    frac.numerator = (int) floor(numerator);

    writes(frac);
    printf("\n");

    simplification(&frac);

    return frac;
}

Будучи «пишет» и «упрощает» еще две написанные мной функции, которые также пишут форматированные дроби и упрощения, но они хорошо протестированы и работают правильно.

Моя основная задача:

int main()
{
    float decimal;

    while (1) {
    printf("Type a decimal number: ");
    scanf("%f", &decimal);
    printf("Typed number: %f\n", decimal);
    writes(ftof(decimal));
    printf("\n\n");
    }
}

С "printf", который я вставил в середину функции, он говорит мне числа, которые он вычисляет (я никогда не понимал, как правильно отлаживать в CodeBlocks), поэтому, когда я печатаю0,25, это приведет к 1/4.Но, если я введу более сложное число, например 0,74, получится:

Type a decimal number: 0,74
Typed number: 0,740000
Number of decimal places: 0
Decimal part: 0,740000

Houses number updated: 1
Decimal part updated: 0,400000
Houses number updated: 2
Decimal part updated: 0,000001
Houses number updated: 3
Decimal part updated: 0,000010
Houses number updated: 4
Decimal part updated: 0,000095
Houses number updated: 5
Decimal part updated: 0,000954
Houses number updated: 6
Decimal part updated: 0,009537
Houses number updated: 7
Decimal part updated: 0,095367
Houses number updated: 8
Decimal part updated: 0,953674
Houses number updated: 9
Decimal part updated: 0,536743
Houses number updated: 10
Decimal part updated: 0,367432
Houses number updated: 11
Decimal part updated: 0,674316
Houses number updated: 12
Decimal part updated: 0,743164
Houses number updated: 13
Decimal part updated: 0,431641
Houses number updated: 14
Decimal part updated: 0,316406
Houses number updated: 15
Decimal part updated: 0,164063
Houses number updated: 16
Decimal part updated: 0,640625
Houses number updated: 17
Decimal part updated: 0,406250
Houses number updated: 18
Decimal part updated: 0,062500
Houses number updated: 19
Decimal part updated: 0,625000
Houses number updated: 20
Decimal part updated: 0,250000
Houses number updated: 21
Decimal part updated: 0,500000
Houses number updated: 22
Decimal part updated: 0,000000
0/0
512/311

Я имею в виду, что когда был только один десятичный знак, получилось 0,4 * 10 = 4, а затем 4 - пол(4) = 0,000001.

Что я могу сделать?

1 Ответ

2 голосов
/ 30 марта 2019

Типы с плавающей запятой обычно используют двоичную мантиссу, что означает, что числа, которые могут быть представлены точно в базе 10, не могут быть точно представлены в базе 2. Число типа 0,25 имеет точное представление в двоичной форме, а 0,4 - нет.Таким образом, вы не можете получить точный ответ.

Чтобы решить эту проблему, нужно не считать число как float, а как строку и выполнить преобразование в рациональное число самостоятельно.

Fraction stof(char *s)
{
    Fraction f;
    long ipart, fpart;
    char *p, *p2;
    int i;

    p = strchr(s, '.');                     // look for the decimal point
    ipart = strtol(s, NULL, 10);            // get the integer part
    if (p) {
        fpart = strtol(p+1, &p2, 10);       // get the fractional part, saving the end pointer
                                            // to count digits in case of leading zeros
    } else {
        fpart = 0;                          // no . found, fractional part is 0
    }
    printf("ipart=%ld, fpart=%ld\n", ipart, fpart);

    f.numerator = ipart;                    // start with just the integer part
    f.denominator = 1;
    for (i=0; i<(p2-p-1); i++) {            // loop for each digit in the fractional part
        f.numerator *= 10;                  // scale up the numerator and denominator
        f.denominator *= 10;
    }
    f.numerator += fpart;                   // add in the fractional part
    printf("fraction = %ld / %ld\n", f.numerator, f.denominator);
    return f;
}
...