Почему строка x = 60 * 1000 / 10 вычисляется некорректно ?

arz_serg
Offline
Зарегистрирован: 05.04.2019

Добрый день!

Помогите пожалуйста понять в чем проблема.

word a;
word b;
word c;
word x;

void setup() {
 Serial.begin(9600);
}

void loop() {
  x = 60000 / 10;
  Serial.println(x); // Выдаёт 6000
  
  x = 60 * 1000 / 10;
  Serial.println(x); // Выдаёт 64983

  a = 60;
  b = 1000;
  c = a*b;
  x = c / 10;
  Serial.println(x); // Выдаёт 6000
  
  delay(1000);
 
}

Почему строка x = 60 * 1000 / 10 вычисляется не корректно?

Что я делаю не так? 

Green
Offline
Зарегистрирован: 01.10.2015

arz_serg пишет:

Что я делаю не так? 

Не учу типы данных, преобразования типов.

b707
Offline
Зарегистрирован: 26.05.2017

arz_serg пишет:

Что я делаю не так? 

Нафига тут word? Замените на uint16t и все будет работать.

На будущее - старайтесь не использовать псевдонимы типов, такие как byte, word, int и тд. Указывайте типы переменных однозначно - uint8t, uint16t, int32t . Этим вы убережете себя от таких вот "сюрпризов", как в коде выше.

arz_serg
Offline
Зарегистрирован: 05.04.2019

Green пишет:

Не учу типы данных, преобразования типов.

а конкретнее?

т.е. для 60000 / 10 тип данных word подходит

а для 60 * 1000 / 10 не подходит?

Green
Offline
Зарегистрирован: 01.10.2015

arz_serg пишет:

т.е. для 60000 / 10 тип данных word подходит

а для 60 * 1000 / 10 не подходит?

Подходит.
Не подходит.
word - это результат, но прежде математика. Распишите типы для всех операндов и поиграйтесь.

b707
Offline
Зарегистрирован: 26.05.2017

arz_serg пишет:

Green пишет:

Не учу типы данных, преобразования типов.

а конкретнее?

т.е. для 60000 / 10 тип данных word подходит

а для 60 * 1000 / 10 не подходит?


че, неужели еще не дошло?

arz_serg
Offline
Зарегистрирован: 05.04.2019

b707 пишет:
arz_serg пишет:

Что я делаю не так? 

Нафига тут word? Замените на uint16t и все будет работать. На будущее - старайтесь не использовать псевдонимы типов, такие как byte, word, int и тд. Указывайте типы переменных однозначно - uint8t, uint16t, int32t . Этим вы убережете себя от таких вот "сюрпризов", как в коде выше.

поставил uint16_t - результат тот же

arz_serg
Offline
Зарегистрирован: 05.04.2019

b707 пишет:
arz_serg пишет:

че, неужели еще не дошло?

нет... мой мозг отказывается воспринимать тот факт, что эти строки могут вычисляться по-разному

прошу пояснений или ссылку на какую-нибудь статью с пояснениями )

b707
Offline
Зарегистрирован: 26.05.2017

arz_serg пишет:

поставил uint16_t - результат тот же


да, я тоже слошил. Строчку 14 вот так запиши:

x = 60 * 1000u / 10;

или вот так:
x = 60 *( 1000 / 10);

теперь догадался?

arz_serg
Offline
Зарегистрирован: 05.04.2019

b707 пишет:
да, я тоже слошил. Строчку 14 вот так запиши: x = 60 * 1000u / 10;

заработало, но я всё равно нифига не понял )

можно пояснения пожалуйста? )

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

arz_serg пишет:

можно пояснения пожалуйста? )

В Си по умолчанию все числовые константы без суффиксов имеют тип int

arz_serg
Offline
Зарегистрирован: 05.04.2019

а суффикс u указывает, что это дробный тип?

т.е. 60 * 1000.0 / 10 - тоже заработает....

но почему 60000 / 10 он нормально делит, без суффиксов?

b707
Offline
Зарегистрирован: 26.05.2017

если по-простому, по умолчанию все вычисления в Си происходят в типе signed  int. Для ардуины это соответвует типу int16t с диапазоном значений - 32768 ... 32767

В твоем выражении

x = 60 * 1000 / 10;

сначала вычисляется произведение, оно сразу превышает предел типа int16 и происходит переполнение

Спецификатор u у любого из операторов сразу переводит выражение в тип uin16. где диапазон уже 0..65536. Твое произведение помещается, в итоге все считается корректно.

Или просто добавить скобки

b707
Offline
Зарегистрирован: 26.05.2017

arz_serg пишет:

а суффикс u указывает, что это дробный тип?

ой....  не надо так откровенно тупить. Есть же гугль. Прежде чем написать глупость, сделал бы поиск

arz_serg
Offline
Зарегистрирован: 05.04.2019

b707 пишет:

если по-простому, по умолчанию все вычисления в Си происходят в типе signed  int. Для ардуины это соответвует типу int16t с диапазоном значений - 32768 ... 32767

В твоем выражении

x = 60 * 1000 / 10;

сначала вычисляется произведение, оно сразу превышает предел типа int16 и происходит переполнение

Спецификатор u у любого из операторов сразу переводит выражение в тип uin16. где диапазон уже 0..65536. Твое произведение помещается, в итоге все считается корректно.

Или просто добавить скобки

 

вот теперь понял, спасибо....

я думал вычисление идёт в том же диапазоне, что и переменная, в которую записывается результат

по крайней мере в pascal'е и delphi (которые я изучал в институте) это работало так, если мне не изменяет память

arz_serg
Offline
Зарегистрирован: 05.04.2019

b707 пишет:

ой....  не надо так откровенно тупить. Есть же гугль. Прежде чем написать глупость, сделал бы поиск

всё всё всё, сообразил... u - unsigned

а 1000.0 - преобразует в float, в который  60000 так же умещается

Ещё раз спасибо, затуп конечно знатный, признаю. Был абсолютно убежден, что вычисления ведутся в диапазоне переменной, в которую записывается результат... это и ввело меня в небывалый ступор )

b707
Offline
Зарегистрирован: 26.05.2017

arz_serg пишет:

Ещё раз спасибо, затуп конечно знатный, признаю. Был абсолютно убежден, что вычисления ведутся в диапазоне переменной, в которую записывается результат... это и ввело меня в небывалый ступор )

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

_Igor_
Offline
Зарегистрирован: 10.01.2022
x = 60 * 1000 / 10;
1. Размер - по минимуму - здесь два байта.
2. Для компилятора это ЗНАКОВЫЕ константы.
60*1000=60000(беззнаковое) <=> -5536(знаковое)
-5536/10=-553(знаковое) <=> 64983(беззнаковое)
Почему печатает беззнаковое - это другой вопрос
Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

_Igor_ потому что результат уходит в word и только потом выводится ...

_Igor_
Offline
Зарегистрирован: 10.01.2022

да

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Почему эта тема все ещё в не в разделе Песочница?)

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

arz_serg пишет:

вот теперь понял, спасибо....

я думал вычисление идёт в том же диапазоне, что и переменная, в которую записывается результат

по крайней мере в pascal'е и delphi (которые я изучал в институте) это работало так, если мне не изменяет память

Неа, не понял ты...

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

arz_serg пишет:

я думал вычисление идёт в том же диапазоне, что и переменная, в которую записывается результат

Значить, Вы не умеете логично думать.

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

arz_serg пишет:

по крайней мере в pascal'е и delphi (которые я изучал в институте) это работало так, если мне не изменяет память

Значит, кроме логики еще и с памятью проблемы.

arz_serg
Offline
Зарегистрирован: 05.04.2019

1. А у вас я вижу проблемы с социальными навыками и недержанием. Непременно нужно пукнуть и сотрясти воздух (в уже решенной теме) своим, несомненно "важным" мнением, дабы всем показать свою восхитительность;

2. В том, в каком диапазоне производятся вычисления нет никакой логической последовательности. Это решение разработчиков, основанное на их каких-то субъективных суждениях. Они могли так же представлять все константы в типе long или char. Такие вещи не выводятся логическими рассуждениями - их просто надо знать.

3. С памятью у меня, как выяснилось, проблемы небольшие. Специально не поленился и скачал паскаль, чтобы перепроверить. Объявляем переменную "a" с типом "word" и пытаемся присвоить ей три разных значения:

a:=(60 * 1000) div 10 - возвращает корректные 6000

a:=(6000 * 1000) div 10 - выдаёт ошибку "constant out of range" - говорящую о том, что значение не помещается в переменную

a:=(6000000 * 1000) div 10 - выдает ошибку Overflow in arithmetic operation

Суть моего "отпечатка в памяти" была в том, что с аналогичными проблемам в паскале я никогда не ставился, так как он при переполнении выдаёт однозначные ошибки, говорящие о том, где переполнение.

Чуть-чуть ошибся в том, что диапазон вычислений не равен диапазону переменной, а он составляет тип longint - поэтому я в него никогда ранее не упирался.

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

И ещё раз отмечу - что в данном случае логика совершенно не при чем, это особенности языков, которые НУЖНО ЗНАТЬ. Так что ваш опус был тут совершенно ни к чему

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

arz_serg пишет:

Суть моего "отпечатка в памяти" была в том, что с аналогичными проблемам в паскале я никогда не ставился

Вот тут и суть - Паскаль это учебный язык, а си++ - боевой.

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

ЗАДАЧА: Прострелить себе ногу.

C: Вы простреливаете себе ногу.

C++: Вы случайно создаете дюжину копий объекта «вы» и всем им простреливаете ногу. Срочная медицинская помощь оказывается невозможной, так как вы не можете разобраться, где настоящие копии, а где — те, что только указывают на них и говорят: «А вот он я!»

Pascal: Компилятор не позволит вам прострелить себе ногу.

 

Feofan
Offline
Зарегистрирован: 28.05.2017

arz_serg пишет:

с аналогичными проблемам в паскале я никогда не ставился, так как он при переполнении выдаёт однозначные ошибки, говорящие о том, где переполнение.

При компиляции кода из #0 и Arduino IDE однозначно предупреждает, говоря о том, где переполнение:
 

:14:10: warning: integer overflow in expression [-Woverflow]
   x = 60 * 1000 / 10;
       ~~~^~~~~~
arz_serg
Offline
Зарегистрирован: 05.04.2019

mykaida пишет:

Вот тут и суть - Паскаль это учебный язык, а си++ - боевой.

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

А уникумы вроде andriano доводят до белого каления, как будто он из мамки вылез и заговорил сразу на языке С

arz_serg
Offline
Зарегистрирован: 05.04.2019

Feofan пишет:

При компиляции кода из #0 и Arduino IDE однозначно предупреждает, говоря о том, где переполнение:

У меня при компиляции никаких сообщений не выдаётся вообще. Если было как вы показываете - я бы может и сам сообразил

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

В настройках: «Сообщения компилятора» -> «Все»