Как стандартизированный способ расчета с плавающей запятой с целыми числами? - PullRequest
0 голосов
/ 01 февраля 2019

Кто-нибудь из вас знает, как это будет рассчитываться в C?

uint8_t samplerate = 200;
uint8_t Result;
Result = 0.5 * samplerate;

Теперь проблема в том, что 0.5 - это число с плавающей запятой, а samplerate - целое число.Result может тогда быть либо 0, потому что 0.5 преобразуется в целое число и поэтому округляется до 0 (Result = 0 * 200 = 0).Или Result может быть 100, потому что компилятор сначала видит 0.5 и конвертирует samplerate в число с плавающей точкой (Result = 0.5 * 200 = 100).

Есть ли стандартизированный способ, которым компилятор будет обрабатывать эти вычисления?Я имею в виду, будет ли компилятор сначала смотреть на переменную слева (в данном случае 0.5) и преобразовывать в нее другую, либо он будет смотреть на переменную справа (samplerate) и преобразовывать другие переменные в эту.?

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

Ответы [ 3 ]

0 голосов
/ 01 февраля 2019

Да, есть стандарт.В этом случае числа в выражении автоматически преобразуются в более широкий тип (который занимает больше байтов), поэтому ваше выражение будет оцениваться следующим образом:

(0.5: double) * (0: uint8_t) => (0.5: double) * (0.0: double) == (0.0: double)
uint8_t Result = (0.0: double) => (0: uint8_t) // this is a forced cast, because Result is of type uint8_t

double шире, чем uint8_t, поэтому (0: uint8_t) равно расширено до (0.0: double).Это приведение не теряет информацию, поскольку double занимает достаточно места для размещения всех данных, хранящихся в uint8_t.

0 голосов
/ 01 февраля 2019

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

Эти преобразования описаны в разделе 6.3.1.8 стандарта C:

Многие операторы, которые ожидают операнды арифметического типа, вызывают преобразования и выдают типы результатов аналогичным образом.Цель состоит в том, чтобы определить общий реальный тип для операндов и результата.Для указанных операндов каждый операнд преобразуется без изменения типа домена в тип, соответствующий действительный тип которого является общим действительным типом.Если явно не указано иное, общий действительный тип также является соответствующим действительным типом результата, чья область типов является областью типов операндов, если они одинаковы, и сложной в противном случае.Этот шаблон называется обычным арифметическим преобразованием:

  • Во-первых, если соответствующий действительный тип одного из операндов long long, другой операнд преобразуется без изменения домена типа в тип, соответствующий действительному веществутип long long double.
  • В противном случае, если соответствующий действительный тип одного из операндов является двойным, другой операнд преобразуется без изменения домена типа в тип, соответствующий действительный тип которого является двойным.
  • В противном случае, если соответствующий действительный тип одного из операндов является плавающим, другой операнд преобразуется без изменения домена типа в тип, соответствующий действительный тип которого является плавающим.
  • В противном случаецелочисленные продвижения выполняются для обоих операндов.Затем к продвигаемым операндам применяются следующие правила:
    • Если оба операнда имеют одинаковый тип, дальнейшее преобразование не требуется.
    • В противном случае, если оба операнда имеют целочисленные типы со знаком или оба имеют целочисленные типы без знака, операнд с типом меньшего целого ранга преобразования преобразуется в тип операнда с большим рангом.
    • В противном случае, если операнд с целым типом без знака имеет ранг, больший или равный рангу типа другого операнда, тогда операнд с целым типом со знаком преобразуется в тип операнда с целым числом без знакатип.
    • В противном случае, если тип операнда с целочисленным типом со знаком может представлять все значения типа операнда с целочисленным типом без знака, то операнд с целочисленным типом без знака преобразуется в тип операндасо знаком целочисленного типа.
    • В противном случае оба операнда преобразуются в целочисленный тип без знака, соответствующий типу операнда с целым типом со знаком.

В частности, обратите вниманиеабзац, выделенный жирным шрифтом, это то, что применимо в вашем случае.

Константа с плавающей точкой 0.5 имеет тип double, поэтому значение другого операнда преобразуется в тип double, а результат оператора умножения * имеет тип double.Затем этот результат присваивается обратно переменной типа uint8_t, поэтому значение double преобразуется в этот тип для присвоения.

Таким образом, в этом случае Result будет иметь значение 100.

0 голосов
/ 01 февраля 2019

Да, конечно, это контролируется стандартом, здесь нет никакой неопределенности.

Обычно целое число будет повышено до double (поскольку тип 0.5 равен double, этоне float), и вычисление произойдет там, тогда результат будет усечен до uint8_t.Как правило, компилятор будет кричать вам за потерю точности.Если этого не произойдет, добавьте дополнительные параметры предупреждения по мере необходимости.

...