Определение размера данных в программировании на C при использовании математических выражений

При использовании математических операторов в программировании на языке C очень важно использовать приведения или правильно определять размер переменной. Мне нужна помощь в этом.

#include  <stdio.h>
#include  <stdint.h>

int main(void)
{
    uint32_t    a;
    uint8_t     b;
    uint8_t     d;
    uint64_t    c;
    float       cd;

    a = 4294967295;
    b = 2;
    d = 2;
    c = a * b * d;
    cd = c;

    printf("%f\n", cd);

    return 0;
}

Переменная результата достаточно велика, чтобы хранить 2 * 2 * uint32_max. Однако я заметил, что переменная b или d должна иметь ширину 64 бита (или использовать приведение), чтобы получить правильный результат. На этот раз я подумал, что математические операции происходят в переменной результата, но похоже, что это не так. Может ли кто-нибудь объяснить мне, какую переменную нужно расширить (b или d) и какова теоретическая основа этого?

Как обстоят дела в дивизии? Должен ли я подумать, хочу ли я разделить 32-битное число на 8-битное. будет ли результат в этом случае только 8-битным? Есть ли какое-либо правило о типе знаменателя?

вы должны прочитать о implicit arithmetic conversions. en.cppreference.com/w/c/language/…

alinsoar 22.05.2019 15:31

Никакая переменная не нуждается в расширении. Вы также можете ввести c = (uint64_t)a * b * c;.

Daniel Langr 22.05.2019 15:32

@ user1810087 Это не «расширение» переменной (в некотором смысле OP использует этот термин). Он просто создает временное значение из приведенного значения переменной, которое остается нетронутым.

Daniel Langr 22.05.2019 15:37
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
1
3
155
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Когда вы выполняете умножение a * b * d, произойдет то, что b и d получат продвинутый для uint32_t (или int, если int шире, чем uint32_t), чтобы соответствовать типу a. Однако эта операция может переполниться. Итак, что вам нужно сделать, это бросить хотя бы один из них на uint_64_t, чтобы этого не произошло.

Обратите внимание, что (uint64_t)(a * b * d) НЕ будет работать. Приведение типов имеет более низкий приоритет, чем круглые скобки.

Более строго тип может быть int, если int шире, чем uint32_t.

Bathsheba 22.05.2019 15:38

@eerorika: Но выражение, состоящее из int и uint32_t, может быть uint32_t.

Bathsheba 22.05.2019 15:40

@ Вирсавия, о да. Я предполагал два uint8_t, что здесь не так.

eerorika 22.05.2019 15:41

@eerorika: Действительно, это тонко - одна из причин, по которой я влез с ответом.

Bathsheba 22.05.2019 15:42

@Bathsheba Вы уверены, что он будет повышен до подписал int?

klutt 22.05.2019 15:50

@Broman: Да, например, тип unsigned char * unsigned char — это int.

Bathsheba 22.05.2019 15:53
b не повышается до uint32_t. целые акции сделает это максимум int. После целочисленного продвижения обычные арифметические преобразования преобразует его в тип a, если a шире, чем int.
Eric Postpischil 22.05.2019 16:18

a * b * d является выражением типа uint32_t или int, если int шире, чем uint32_t (из-за правила преобразования для uint8_t).

Тот факт, что это выражение присвоено более широкому типу, не имеет значения. Это суть вопроса.

Написание c = 1ULL * a * b * d — это легкое исправление.

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

Eric Postpischil 22.05.2019 16:17

@EricPostpischil: это то, что я хотел сказать, но, по-видимому, это недостаточно ясно.

Bathsheba 22.05.2019 16:18
Ответ принят как подходящий

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

Выражение a * b * d структурировано как (a * b) * d. Итак, вычисляется a * b, а затем результат умножается на d.

Одно из правил для * есть в C 2018 6.5.5 3:

The usual arithmetic conversions are performed on the operands.

обычные арифметические преобразования определены в 6.3.1.8 1. Они немного сложны, и я даю большую часть деталей ниже. Применяя их к вашему примеру:

  • В a * ba — это uint32_t, а b — это uint8_t.
  • целые акции преобразует b в int — по сути, вся арифметика в C выполняется с шириной не менее int.
  • Если int составляет 32 бита или меньше, a остается uint32_t. В противном случае a преобразуется в int.
  • Если преобразованные типы a и b оба являются int, преобразования выполняются и выполняется умножение.
  • Если преобразованный тип a равен uint32_t, b преобразуется в uint32_t и выполняется умножение.
  • Затем таким же образом выполняется умножение на d.

Таким образом, если int составляет 32 бита или меньше, умножение выполняется с uint32_t, и результат равен uint32_t. Если int шире, умножение выполняется на int, и результат равен int.

Приведение любого операнда к uint64_t приведет к тому, что арифметика будет выполняться с uint64_t. (За исключением того, что теоретически возможно, что int шире, чем uint64_t, и в этом случае арифметика будет выполняться с int, но это все же удовлетворительно - выполнение приведения гарантирует, что арифметика будет выполнена по крайней мере с этой шириной.)

Для действительных чисел обычные арифметические преобразования в основном таковы:

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

Целочисленные продвижения определены в 6.3.1.1 2. Они применяются ко всем целочисленным типам такой же ширины, как int или unsigned int (технически с рангом меньше или равным рангу int и unsigned int), включая битовые поля типа _Bool , int, signed int или unsigned int.

If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.

Лучший ответ, который я видел за всю неделю.

Bathsheba 22.05.2019 16:35

Это большой объем информации, и мне потребовалось время, чтобы понять, но я очень благодарен за помощь. Первоначально моей целью было сэкономить место в среде 8-битного микропроцессора, поэтому я пытался использовать как можно более узкие переменные, которые могут хранить их значение. В дальнейшем учту вышеизложенные советы.

T.Barta 23.05.2019 16:21

Другие вопросы по теме