Неявное преобразование типов в C - PullRequest
7 голосов
/ 10 ноября 2011

Я наткнулся на следующий пример в Википедии (http://en.wikipedia.org/wiki/Type_conversion#Implicit_type_conversion).

#include <stdio.h>

int main()
{
    int i_value   = 16777217;
    float f_value = 16777217.0;
    printf("The integer is: %i\n", i_value); // 16777217
    printf("The float is:   %f\n", f_value); // 16777216.000000
    printf("Their equality: %i\n", i_value == f_value); // result is 0
}

Их объяснение: «Это странное поведение вызвано неявным приведением i_value к float, когда оно сравнивается с f_value; приведениемкоторый теряет точность, что делает сравниваемые значения разными. "

Разве это не неправильно? Если бы i_value был приведен к float, то оба имели бы одинаковую потерю в точности, и они были бы равны. Поэтому i_value должен бытьприведение к удвоению.

Ответы [ 3 ]

7 голосов
/ 10 ноября 2011

Нет, в случае оператора равенства происходят "обычные арифметические преобразования" , которые начинаются с:

  • Сначала, если соответствующий вещественный типлюбого из операндов равен long double, другой операнд преобразуется без изменения домена типа в тип, соответствующий действительный тип которого равен long double.
  • В противном случае, если соответствующий действительный тип любого из операндов равен double, другой операнд преобразуется без изменения домена типа в тип, соответствующий действительный тип которого равен double.
  • В противном случае, если соответствующий действительный тип одного из операндов равен float, другойОперанд преобразуется без изменения домена типа в тип, соответствующий действительный тип которого равен float.

Этот последний случай применяется здесь: i_value преобразуется в float.

Причина, по которой вы можете увидеть странный результат сравнения, несмотря на , это из-за этого предостережения в обычные арифметические преобразования:

Значенияиз плавающих операндов и результатов плавающих выражений могут быть представлены с большей точностью и диапазоном, чем требуется типом;типы не меняются.

Вот что происходит: тип преобразованного i_value по-прежнему float, но в этом выражении ваш компилятор использует эту широту и представляетэто с большей точностью, чем float.Это типичное поведение компилятора при компиляции для 387-совместимой плавающей запятой, потому что компилятор оставляет временные значения в стеке с плавающей запятой, который хранит числа с плавающей запятой в формате расширенной точности 80 бит.*, вы можете отключить эту дополнительную точность, задав опцию командной строки -ffloat-store.

0 голосов
/ 10 ноября 2011

Здесь есть несколько хороших ответов.Вы должны быть очень осторожны при преобразовании различных целых чисел и различных представлений с плавающей запятой.

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

0 голосов
/ 10 ноября 2011

Я полагаю, что наибольшее целочисленное значение, которое может содержать 32-разрядное число с плавающей запятой IEEE, равно 1048576, что меньше числа, указанного выше. Таким образом, определенно верно, что значение с плавающей запятой не будет содержать точно 16777217.

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

1) Преобразовать оба значения в «float» (это должно сделать значения одинаковыми, так что это, вероятно, не то, что делает компилятор)

2) Преобразовать оба значения в «int» (это может показывать или не отображать их одинаково ... преобразование в int часто усекается, поэтому, если значение с плавающей запятой равно 16777216.99999, преобразование в «int» будет усекать )

3) Преобразовать оба значения в «double». Я думаю, что это то, что будет делать компилятор. Если это то, что делает компилятор, два значения определенно будут разными. Двойной тип может содержать 16777217 в точности, и он также может точно представлять значение с плавающей запятой, в которое конвертируется 16777217.0 (что не совсем 16777217.0).

...