2016-11-14 3 views
0

Мне нужно выполнить простое умножение 400 * 256.3. Результат 102520. Прямо вперед и просто. Но реализовать это умножение в C++ (или C) немного сложно и сбивает с толку.Рекомендации по умножению поплавка в C++ или C?

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

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

Я знал, что математика с плавающей запятой, выполненная компьютером, вообще не сломана. Но мне любопытно, как лучше всего выполнять математику с плавающей запятой. 256.3 - это просто значение для иллюстрации. Я не знаю, какое значение с плавающей запятой я получу во время выполнения. Но это точно, значение с плавающей запятой.

int main() 
{ 
    //perform 400 * 256.3 
    //result should be 102520 

    float floatResult = 0.00f; 
    int intResult = 0; 
    double doubleResult = 0.00; 

    //float = int * float 
    floatResult = 400 * 256.3f; 
    printf("400 * 256.3f = (float)->%f\n", floatResult); 

    //float = float * float 
    floatResult = 400.00f * 256.3f; 
    printf("400.00f * 256.3f = (float)->%f\n", floatResult); 

    printf("\n"); 

    //int = int * float 
    intResult = 400 * 256.3f; 
    printf("400 * 256.3f = (int)->%d\n", intResult); 

    //int = float * float; 
    intResult = 400.00f * 256.3f; 
    printf("400.00f * 256.3f = (int)->%d\n", intResult); 

    printf("\n"); 

    //double = double * double 
    doubleResult = 400.00 * 256.3; 
    printf("400.00 * 256.3 = (double)->%f\n", doubleResult); 

    //int = double * double; 
    intResult = 400.00 * 256.3; 
    printf("400.00 * 256.3 = (int)->%d\n", intResult); 

    printf("\n"); 

    //double = int * double 
    doubleResult = 400 * 256.3; 
    printf("400 * 256.3 = (double)->%f\n", doubleResult); 

    //int = int * double 
    intResult = 400 * 256.3; 
    printf("400 * 256.3 = (int)->%d\n", intResult); 

    printf("\n"); 

    //will double give me rounding error? 
    if (((400.00 * 256.3) - 102520) != 0) { 
     printf("Double give me rounding error!\n"); 
    } 

    //will float give me rounding error? 
    if (((400.00f * 256.3f) - 102520) != 0) { 
     printf("Float give me rounding error!\n"); 
    } 

    return 0; 
} 

Output from the code above

+3

Ответ прост: вы не можете. Проблема округления «встроена» на компьютерах с использованием формата с плавающей запятой IEEE, что почти все. –

+2

Возможный дубликат [Является ли математика с плавающей запятой?] (Http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Olaf

+0

Значение 256.3f также лежит на вас, и вы не можете избежать этого , – 2501

ответ

5

Если у вас есть фиксированное количество десятичных цифр (1 в случае 256.3), а также ограниченный диапазон результатов, вы можете использовать целое умножение, а также настроить для сдвига в десятичных цифрах через целочисленное деление:

int result = (400 * 2563)/10; 

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

+1

Большое спасибо за предложение! Но что, если я не знаю значения операнда до времени выполнения? –

+0

Что вы знаете о поплавковых входах? Каковы их [домен] (https://en.wikipedia.org/wiki/Domain_of_a_function)? Почему ошибки округления являются проблемой? – TheOperator

1

Прежде всего, поймите, что тип double имеет все те же проблемы, что и тип float. Ни один из типов не имеет бесконечной точности, поэтому оба типа подвержены прецизионным потерям и другим проблемам.

Что касается того, что вы можете сделать: есть много разных проблем, которые возникают, в зависимости от того, что вы делаете, и многих методов для их преодоления. На эти методы написано много, много слов; Я предлагаю сделать веб-поиск «избегая ошибки с плавающей запятой». Но основные моменты:

  • Знайте, что результаты с плавающей запятой никогда не точны
  • Не пытайтесь сравнивать числа с плавающей точкой для точного равенства
  • числа
  • При сравнении с плавающей точкой на равенство, использование соответствующий «эпсилон» диапазон
  • После расчета, часто бывает целесообразный явно вокруг конечного значения до требуемой точности (особенно при печати его)
  • Остерегайтесь алгоритмы, которые вызывают потерю точности по увеличению с каждым шагом

См. Также https://www.eskimo.com/~scs/cclass/handouts/sciprog.html.

+0

Большое спасибо за ваше предложение! Я ценю! :) –

1

Ключевым недостатком, связанным с отображением проблемы, является преобразование в int intResult.Проблема связана с умножением и по сравнению, но код только показывает проблемы, связанные с int преобразования.

Если код необходимо преобразовать значение FP до целого числа ближайшего, использует rint(), round(), nearbyint() или lround(), не целое назначение.

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