2010-03-24 4 views
2

Я попробовал следующий фрагмент кода и вывод удивляет меня:печать содержание двойной переменной

#include <stdio.h> 
#include <math.h> 

int main() 
{ 
      double num; 
      unsigned char ch; 

      ch = 19; 
      num = 1.0E+20 ; 
      num += ch * 1.0E+18; 
      printf("E18 = %lf \n",num); 
      printf("E18 = %e \n",num); 

      num = 11.0E+21 ; 
      num += ch * 1.0E+19; 
      printf("E19 = %lf <------\n",num); 
      printf("E19 = %e <------\n",num); 

      num = 11.0E+22 ; 
      num += ch * 1.0E+20; 
      printf("E20 = %lf\n",num); 
      printf("E20 = %e\n",num); 

      num = 11.0E+23 ; 
      num += ch * 1.0E+21; 
      printf("E21 = %lf\n",num); 
      printf("E21 = %e\n",num); 

      num = 11.0E+24 ; 
      num += ch * 1.0E+22; 
      printf("E22 = %lf <------\n",num); 
      printf("E22 = %e <------\n",num); 
    return 0; 
} 

Выход программы:

E18 = 119000000000000000000.000000 
E18 = 1.190000e+20 
E19 = 11190000000000000524288.000000 <------ 
E19 = 1.119000e+22 <------ 
E20 = 111900000000000001048576.000000 
E20 = 1.119000e+23 
E21 = 1119000000000000044040192.000000 
E21 = 1.119000e+24 
E22 = 11189999999999999366660096.000000 <------ 
E22 = 1.119000e+25 <------ 

Почему данные испорчены при печати в то время как в показателе формируют его OK

ответ

0

Double/чисел с плавающей точкой теряют точность, поскольку они получают больше - в дополнение к the Wikipedia article Туомаса отправил, вот еще один хороший один:

http://www.yoda.arachsys.com/csharp/floatingpoint.html

Он был нацелен на .NET, но принцип все еще применяется.

3

Данные не повреждены; это просто то, как значения с плавающей запятой работают на современных компьютерах. Подумайте о поплавке как исправлено количество цифр (мантисса) и другое число, которое указывает, где должна быть размещена десятичная точка (экспонента). Для длинной и более точной истории, Wikipedia is a good start.

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

Экспоненциальная нотация просто округляет последние несколько цифр, где число, как известно, является неточным, следовательно, ваши результаты.

0

Типы данных с плавающей точкой используют конечное число бит для представления определенного диапазона чисел. Однако между любыми двумя действительными числами m и n может быть бесконечное число значений. Итак, вы жертвуете точностью.

Он выглядит хорошо в экспоненциальной форме, так как не все цифры печатаются.

В качестве теста, попробуйте напечатать значение 0,2 с около 10 знаков после запятой, и вы увидите, что значение, сохраненное больше похож 0.1999999 ....

0

Вы видите неточность, присущую с плавающей точкой.

Определенные printf конверсии гарантируют получение значительных цифр, чтобы однозначно идентифицировать напечатанное число. Это означает, что если есть какая-либо неточность, вы увидите это. И наоборот, значение по умолчанию %e/%f скрывает неточность путем округления.

Насколько я знаю, %a (шестнадцатеричная плавающая точка) - единственный способ достичь этого. Согласно the POSIX spec, %lf определяется сделать то же самое, как %f, то есть

l (флигель) ... не имеет никакого эффекта на следующий а, А, Е, Е, F, F, G, или G.

Так что это технически ошибка в стандартной библиотеке.

Смежные вопросы