Ошибка из-за ограниченной точности float и double

#c #floating-accuracy #double-precision

#c #плавающая точность #двойная точность

Вопрос:

В C я использую следующий код для определения порядка величины ошибки из-за ограниченной точности float и double:

  float n=1;
 float dec  = 1;

 while(n!=(n-dec)) {
    dec = dec/10;
 }
 cout << dec << endl;
  

(в двойном случае все, что я делаю, это обмениваю float на double в строке 1 и 2)

Теперь, когда я компилирую и запускаю это с помощью g в системе Unix, результаты

 Float  10^-8
Double 10^-17
  

Однако, когда я компилирую и запускаю его с помощью MinGW в Windows 7, результаты

 Float  10^-20
Double 10^-20
  

В чем причина этого?

Комментарии:

1. Что-то подсказывает мне, что MinGW хранит промежуточные n!=(n-dec) данные с 80-битной расширенной точностью. 10^-20 примерно равен эпсилону 80-битного FP…

Ответ №1:

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

MinGW в Windows, вероятно, пытается сохранить точность, повышая промежуточные выражения до полной 80-битной точности x86.

Следовательно, обе части выражения n != (n-dec) вычисляются с точностью до 64 бит (80-битный FP имеет 64-битную мантиссу).

 2^-64 ~ 10^-20
  

Таким образом, числа имеют смысл.

Visual Studio также (по умолчанию) будет продвигать промежуточные элементы. Но только до двойной точности.

Комментарии:

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

2. @edA-qa морт-ора-у: Согласен. Я подумал об этом немного больше. Все, что нужно, это чтобы режим FP был с повышенной точностью. Код также достаточно мал, чтобы не пропускать регистры x87. Таким образом, округления в хранилище не произойдет.

Ответ №2:

Почему вы не проверяете размер float и double в обеих ОС?

Комментарии:

1. На W7 это будет float = 32bit, double = 64bit (с использованием sizeof() ). Как мне связать это с моими выводами сверху?

Ответ №3:

Это просто показывает, что разные среды используют разные размеры для float и double.

Согласно спецификации C , double должен быть как минимум таким же большим, как float . Если вы хотите узнать, насколько велики типы в вашей системе, используйте sizeof .

Похоже, что ваши тесты указывают на то, что g использует отдельные размеры для float и double (32 и 64 бита соответственно), в то время как MinGW32 в вашей системе Windows использует одинаковый размер для обоих. Обе версии соответствуют стандарту, и на поведение вообще нельзя полагаться.

Комментарии:

1. Смотрите мой комментарий к сообщению suresh ниже. Размеры, используемые в моей системе Windows для float и double, разные.

2. @Agentlien: в обеих системах float значение равно 4 байтам и double равно 8 байтам. При отключенной оптимизации MinGW ведет себя точно так же, как Unix.