2016-10-21 7 views
4

Я знаю, что значения с плавающей запятой ограничены в числах, которые могут выражаться точно, и я нашел много сайтов, которые описывают, почему это происходит. Но я не нашел никакой информации о том, как эффективно решать эту проблему. Но я уверен, что НАСА не в порядке с 0.2/0.1 = 0.199999. Пример:Неточности с плавающей запятой в c

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

int main(void) 
{ 
    float number = 4.20; 
    float denominator = 0.25; 

    printf("number = %f\n", number); 
    printf("denominator = %f\n", denominator); 
    printf("quotient as a float = %f should be 16.8\n", number/denominator); 
    printf("the remainder of 4.20/0.25 = %f\n", number - ((int) number/denominator)*denominator); 
    printf("now if i divide 0.20000 by 0.1 i get %f not 2\n", (number - ((int) number/denominator)*denominator)/0.1); 
} 

выход:

number = 4.200000 
denominator = 0.250000 
quotient as a float = 16.799999 should be 16.8 
the remainder of 4.20/0.25 = 0.200000 
now if i divide 0.20000 by 0.1 i get 1.999998 not 2 

Так как я делаю арифметику с плавающей точкой (или знаков после запятой или два раза) и получить точные результаты. Надеюсь, я не просто пропустил что-то сверх очевидное. Любая помощь была бы потрясающей! Благодарю.

+7

Довольно точно, что NASA не использует переменные 'float' для межпланетной навигации. Однако они могут использовать «double». Если вы настроили сетку с центром на солнце и простираетесь до Марса, разрешение сетки составляет около 50 микрон (используя «double» для значений x, y, z). Таким образом, «двойник» достаточно хорош для навигации на Марс. И именно так люди справляются с этим. Изучите ваши требования и выберите подходящий инструмент для работы. Если 'float' недостаточно хорош, используйте' double'. Если 'double' недостаточно хорош, вам понадобится библиотека расширенной точности. – user3386109

+0

'double' следует использовать по умолчанию, а не' float'. Вот почему литералы с плавающей запятой без суффикса 'f' будут' double' –

+7

Довольно уверены, что NASA в порядке с числами с плавающей запятой, если ваш алгоритм численно стабилен. Существует целая область исследования под названием «численный анализ», которая посвящена пониманию проблем, связанных с точностью численных алгоритмов и ошибок округления. Однако, если вы пишете '0,2/0,1', вы должны * всегда * получить' 2.0', точно. Если вы не получите '2.0' именно тогда, вы не начали с правильно округленных версий' 0,2' и '0,1', вы начали с чего-то другого. Причина, по которой вы не получаете '2.0', на самом деле потому, что' 4.2 - 4.0! = 0.2' - вот где ошибка. –

ответ

3

Решение состоит в том, чтобы не использовать поплавки для приложений, где вы не можете принимать ошибки округления. Используйте библиотеку с расширенной точностью (библиотека произвольной точности a.k.a.), например GNU MP Bignum. См. this Wikipedia page за хороший список библиотек произвольной точности. См. Также статью Википедии о rational data types и this thread для получения дополнительной информации.

Если вы собираетесь использовать представление с плавающей запятой (float, double и т.д.), а затем написать код, используя общепринятые методы для решения ошибок округления (например, избегая ==). Существует много онлайн-литературы о том, как это сделать, и методы широко варьируются в зависимости от используемого приложения и алгоритмов.

0

Существует не очень хороший ответ, и это часто проблема.

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

Как правило, вы получаете неточности при попытке деления на числа, близкие к нулю. Поэтому вам просто нужно написать алгоритмы, чтобы избежать или пресечь такие операции. Существует множество обсуждений «численно стабильных» и «неустойчивых» алгоритмов, и это слишком большой вопрос, чтобы отдать ему должное. И тогда, как правило, лучше всего обрабатывать числа с плавающей запятой, как если бы они имели небольшие случайные ошибки. Если они в конечном итоге представляют измерения аналоговых значений в реальном мире, в любом случае в них должна быть определенная толерантность или неточность.

Если вы выполняете математику, а не обрабатываете данные, просто не используйте C или C++. Используйте пакет символической алгебры, такой как Maple, который сохраняет значения, такие как sqrt (2), как выражение, а не число с плавающей запятой, поэтому sqrt (2) * sqrt (2) всегда будет давать ровно 2, а не число очень близко до 2.

1

Плавающая точка довольно хорошо, большую часть времени. Вот ключевые моменты, я стараюсь, чтобы иметь в виду:

  • Там действительно большая разница между float и double. double дает вам достаточную точность для большинства вещей, большую часть времени; float на удивление часто дает вам недостаточно. Если вы не знаете, что делаете, и у вас есть веская причина, просто используйте double.

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

  • Алгоритм, который вы используете, может иметь значение. Наивные или «очевидные» алгоритмы могут легко привести к увеличению эффекта ошибки округления, в то время как более сложные алгоритмы минимизируют их. Одним простым примером является то, что порядок, в котором вы добавляете числа с плавающей запятой, может иметь значение.

  • Не беспокойтесь о 16.8 по сравнению с 16.799999. Такое бывает всегда, но это не проблема, если только вы не станете проблемой. Если вы хотите, чтобы одно место прошло после десятичного знака, просто распечатайте его, используя %.1f, и printf обойдется вам. (Также не пытайтесь сравнивать числа с плавающей запятой для точного равенства, но я предполагаю, что вы уже слышали об этом.)

  • В связи с вышеизложенным помните, что 0,1 не представляется точно в двоичном виде (так же, как и 1/3 не представляется точно в десятичной форме). Это всего лишь одна из многих причин, по которым вы всегда будете получать то, что выглядит как крошечные «ошибки» округления, хотя они совершенно нормальные и не требуют проблем.

  • Иногда вам нужна библиотека с несколькими точками (MP или «bignum»), которая может представлять цифры произвольной точностью, но они относительно медленны и (относительно) громоздки в использовании, и, к счастью, вы, как правило, не используете нужно их. Но хорошо знать, что они существуют, и если вы математика, они могут быть очень полезными в использовании.

  • Иногда полезно использовать библиотеку для представления рациональных чисел. Такая библиотека представляет, например, число 1/3 как пару чисел (1, 3), поэтому у нее нет неточностей, присущих попытке представить это число как 0.333333333.

Другие рекомендовали бумаги Что каждый компьютер ученый должен знать о арифметики с плавающей точкой, что очень хорошо, и стандартным справочным, хотя это долго и довольно технический характер. Более легкое и более короткое чтение, которое я могу порекомендовать, - это раздаточный материал из класса, который я использовал для обучения: https://www.eskimo.com/~scs/cclass/handouts/sciprog.html#precision. Это немного датировано к настоящему времени, но вы должны начать с основ.

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