точное представление с плавающей точкой в ​​с - PullRequest
5 голосов
/ 29 ноября 2010
void main()
{
    float a = 0.7;

    if (a < 0.7)
        printf("c");
    else
        printf("c++");
} 

В приведенном выше вопросе для 0.7 будет напечатано «c», а для 0.8 - «c ++».Почему?

И как любое число с плавающей запятой представлено в двоичной форме?

В некоторых местах упоминается, что внутри 0,7 будет храниться как 0,699997, но 0,8 как 0,8000011.Почему так?

Ответы [ 9 ]

12 голосов
/ 29 ноября 2010

в основном с помощью float вы получаете 32 бита, которые кодируют

VALUE   = SIGN * MANTISSA * 2 ^ (128 - EXPONENT)
32-bits = 1-bit  23-bits               8-bits

и сохраняется как

MSB                    LSB
[SIGN][EXPONENT][MANTISSA]

, поскольку вы получаете только 23 бита, это количество «точности», которое вы можете хранить. Если вы пытаетесь представить дробь, которая является иррациональной (или повторяющейся) в основании 2, последовательность битов будет округлена до 23-го бита.

0,7 основание 10 равно 7/10, что в двоичном виде 0b111 / 0b1010, вы получите:

0.1011001100110011001100110011001100110011001100110011... etc

Поскольку это повторяется, с фиксированной точностью невозможно точно представить это. то же самое касается 0,8, который в двоичном виде:

0.1100110011001100110011001100110011001100110011001101... etc

Чтобы увидеть, каково значение этих чисел с фиксированной точностью, вам нужно «обрезать их» по количеству битов, которые вы выполняете, и выполнить математику. Единственная хитрость в том, что первая цифра подразумевается и не сохраняется, так что технически вы получаете дополнительный бит точности. Из-за округления последний бит будет равен 1 или 0 в зависимости от значения усеченного бита.

Таким образом, значение 0,7 фактически равно 11 744 051/2 ^ 24 (без эффекта округления) = 0,699999988, а значение 0,8 составляет 13 421 773/2 ^ 24 (с округлением в большую сторону) = 0,800000012.

Это все, что нужно сделать:)

10 голосов
/ 29 ноября 2010

Хорошая ссылка для этого - Что должен знать каждый компьютерный специалист об арифметике с плавающей точкой .Вы можете использовать типы с более высокой точностью (например, double) или двоично-десятичную (BCD) библиотеку, чтобы добиться лучшей точности с плавающей запятой, если вам это нужно.

4 голосов
/ 29 ноября 2010

Внутреннее представление: IEE754 .

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

0 голосов
/ 31 марта 2014

0,7 - числовой литерал; его значение - математическое действительное число 0,7, округленное до ближайшего двойного значения.

После инициализации числа с плавающей точкой a = 0,7 значение a округляется до числа 0,7, то есть действительное число 0,7, округленное до ближайшего двойного значения, округленное до ближайшего значения с плавающей точкой. За исключением огромного совпадения, вы не ожидаете, что будет равно 0,7.

"if (a <0.7)" сравнивает 0, округленное до двойного, затем с плавающей запятой с числом 0,7, округленным до двойного. Кажется, что в случае 0,7 округление производило меньшее число. И в том же эксперименте с 0,8 округление 0,8 до float даст большее число, чем 0,8. </p>

0 голосов
/ 29 ноября 2010

Числа с плавающей точкой в ​​C / C ++ представлены в стандартном формате IEEE-754.В Интернете есть много статей, которые описывают гораздо лучше, чем я, здесь, как именно с плавающей запятой представляется в двоичном виде.Простой поиск IEEE-754 должен осветить тайну.

0 голосов
/ 29 ноября 2010

Другим хорошим ресурсом, чтобы увидеть, как числа с плавающей запятой хранятся в компьютерах как двоичные, является страница Википедии на IEEE-754 .

0 голосов
/ 29 ноября 2010

Если вы хотите узнать, как float / double представлен в C (и почти на всех языках), обратитесь к Стандарту арифметики с плавающей точкой (IEEE 754) http://en.wikipedia.org/wiki/IEEE_754-2008

Using single-precision floats as an example, here is the bit layout:  
seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm    meaning  
31                              0    bit #  
s = sign bit, e = exponent, m = mantissa
0 голосов
/ 29 ноября 2010

float с будут сохранены, как описано в IEEE 754: 1 бит для знака, 8 для смещенного показателя, а остальные - для дробной части.

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

«Что должен знать каждый компьютерщик об арифметике с плавающей запятой» должен подробно ответить на все ваши вопросы.

0 голосов
/ 29 ноября 2010

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

Попробуйте IEEE-754 с плавающей запятой и посмотрите, что вы получите. :)

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