Тип C (C ++, Objective-C) от float к int приводит к уменьшению int - PullRequest
3 голосов
/ 22 ноября 2011

Я написал приложение для iPhone и столкнулся с проблемой, связанной с типизацией между float и int. Я переписал код на C, и результаты всегда были одинаковыми, независимо от того, скомпилирован ли он под OS X (Console и Xcode), Linux (Console) или Windows (Visual Studio):

// Calculation of a page index depending on the amount of pages, its x-offset on the view
// and the total width of the view
#include <stdio.h>

int main()
{
    int   result   = 0;
    int   pagesCnt = 23;
    float offsetX  = 2142.0f;
    float width    = 7038.0f;

    offsetX = offsetX / width;
    offsetX = (float)pagesCnt * offsetX;
    result  = (int)offsetX;

    printf("%f (%d)\n", offsetX, result);
    // The console should show "7.000000 (7)" now
    // But actually I read "7.000000 (6)"

    return 0;
}

Конечно, у нас потеря точности здесь. При выполнении математики с калькулятором x приводит к 7.00000000000008, а не только к 7.000000.

Тем не менее, насколько я понимаю C, это не должно быть проблемой, так как C предназначен для усечения позиций после точки числа с плавающей запятой при преобразовании его в целое число. В данном случае это то, чего я на самом деле хочу.

При использовании double вместо float результат будет таким, как ожидалось, но мне здесь не нужна двойная точность, и это не должно иметь никакого значения.

Я что-то здесь не так? Есть ли ситуации, когда мне следует избегать преобразования в int с помощью (int)? В любом случае, почему С уменьшает результат? Я должен всегда использовать double?

Большое спасибо!

Редактировать: Я что-то неправильно сформулировал: конечно, это имеет значение при использовании double вместо float. Я имел в виду, что это не должно иметь значения для (int) 7.000000f или 7.00000000000008.

Редактировать 2: Благодаря вам, теперь я понимаю мою ошибку. Использование printf () без определения точности с плавающей точкой было неправильным. (int) от 6.99 ... до 6, конечно, правильно. Поэтому я буду использовать double в будущем, когда буду сталкиваться с такими проблемами.

Ответы [ 3 ]

4 голосов
/ 22 ноября 2011

Когда я увеличиваю точность вывода, результат, который я получаю от вашей программы, равен 6.99999952316284179688 (6), поэтому 6 кажется правильным.

1 голос
/ 22 ноября 2011

Во многих случаях компилятор не может изменять порядок операций с плавающей запятой, поскольку арифметика с конечной точностью, как правило, не ассоциативна. то есть.,

(23 * 2142) / 7038 = 7.00000000000000000000

(2142 / 7038) * 23 = 6.99999999999999999979

Это не результаты с одинарной точностью, но это показывает, что двойная точность также не решит вашу проблему.

1 голос
/ 22 ноября 2011

Если вы измените printf("%f (%d)\n", offsetX, result); на printf("%.8f (%d)\n", offsetX, result);, вы увидите проблему. После внесения этого изменения вы получите:

6.99999952 (6)

Вы можете исправить это, округлив вместо приведения к int. То есть измените result = (int)offsetX; на result = roundf(offsetX);. Не забудьте #include <math.h>. Теперь вывод:

6.99999952 (7)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...