Глюк или фича?
От: 7C  
Дата: 13.06.07 07:14
Оценка:
Наткнулся на неожиданное поведение вычислений в программе:

int main()
{
    unsigned int z = 176400;
    long a = -4;
    long b = a*z/1000; //b=4294261
    long c = a*z; // c=-705600
    long d = c/1000; // d =-705
    return 0;
}

В комментариях — текущий результат вычисления.
Компилятор Visual C++ 2005
Почему b не равно d
Кстати, если убрать unsigned (т.е. сделать z знаковым), то b =-705.
Re: Глюк или фича?
От: kankan Украина  
Дата: 13.06.07 07:30
Оценка:
Здравствуйте, 7C, Вы писали:

7C>Наткнулся на неожиданное поведение вычислений в программе:


7C>
7C>int main()
7C>{
7C>    unsigned int z = 176400;
7C>    long a = -4;

        //b - это преобразованный к long результат деления беззнакового a*z на 1000
        // тоесть 0xfff53bc0 сначала делится на 1000, а потом кастится к знаковому
7C>    long b = a*z/1000; //b=4294261

        //здесь беззнаковое a*z (0xfff53bc0) сначала кастится к long(-705600), а потом аж делится на тыщу
7C>    long c = a*z; // c=-705600
7C>    long d = c/1000; // d =-705
7C>    return 0;
7C>}
7C>

7C>В комментариях — текущий результат вычисления.
7C>Компилятор Visual C++ 2005
7C>Почему b не равно d

7C>Кстати, если убрать unsigned (т.е. сделать z знаковым), то b =-705.
Re: Глюк или фича?
От: Seon  
Дата: 13.06.07 07:36
Оценка:
Здравствуйте, 7C, Вы писали:

7C>Наткнулся на неожиданное поведение вычислений в программе:


7C>
7C>int main()
7C>{
7C>    unsigned int z = 176400;
7C>    long a = -4;
7C>    long b = a*z/1000; //b=4294261
7C>    long c = a*z; // c=-705600
7C>    long d = c/1000; // d =-705
7C>    return 0;
7C>}
7C>

7C>В комментариях — текущий результат вычисления.
7C>Компилятор Visual C++ 2005
7C>Почему b не равно d
7C>Кстати, если убрать unsigned (т.е. сделать z знаковым), то b =-705.

Переставь а и z местами.
Re[2]: Глюк или фича?
От: Нестеренко Сергей Украина  
Дата: 13.06.07 07:40
Оценка:
Здравствуйте, Seon, Вы писали:

S>Здравствуйте, 7C, Вы писали:


7C>>Наткнулся на неожиданное поведение вычислений в программе:


7C>>
7C>>int main()
7C>>{
7C>>    unsigned int z = 176400;
7C>>    long a = -4;
7C>>    long b = a*z/1000; //b=4294261
7C>>    long c = a*z; // c=-705600
7C>>    long d = c/1000; // d =-705
7C>>    return 0;
7C>>}
7C>>

7C>>В комментариях — текущий результат вычисления.
7C>>Компилятор Visual C++ 2005
7C>>Почему b не равно d
7C>>Кстати, если убрать unsigned (т.е. сделать z знаковым), то b =-705.

S>Переставь а и z местами.

Не выйдет. Нужно явно привести типы. long b = (int)(a*z)/1000;
Re[2]: Глюк или фича?
От: 7C  
Дата: 13.06.07 08:12
Оценка:
Здравствуйте, kankan, Вы писали:


K> //b — это преобразованный к long результат деления беззнакового a*z на 1000

K> // тоесть 0xfff53bc0 сначала делится на 1000, а потом кастится к знаковому

Я всю жизнь считал, что тип определяется первым операндом умножения (и деления), т.е. тип a*z должен быть long!
Re[3]: Глюк или фича?
От: 7C  
Дата: 13.06.07 08:15
Оценка:
Здравствуйте, 7C, Вы писали:

7C>Здравствуйте, kankan, Вы писали:



K>> //b — это преобразованный к long результат деления беззнакового a*z на 1000

K>> // тоесть 0xfff53bc0 сначала делится на 1000, а потом кастится к знаковому

7C>Я всю жизнь считал, что тип определяется первым операндом умножения (и деления), т.е. тип a*z должен быть long!

Тем более, что
 long c = a*z; // c=-705600

вычисляется правильно
Re[3]: Глюк или фича?
От: Bell Россия  
Дата: 13.06.07 08:32
Оценка:
Здравствуйте, 7C, Вы писали:

7C>Я всю жизнь считал, что тип определяется первым операндом умножения (и деления), т.е. тип a*z должен быть long!

Это неверно. Тип выражения определяется типом "максимального" операнда, и при этом совершенно неважно, на каком месте в выражении он находится.

5/9
Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield
result types in a similar way. The purpose is to yield a common type, which is also the type of the result.
This pattern is called the usual arithmetic conversions, which are defined as follows:
— If either operand is of type long double, the other shall be converted to long double.
— Otherwise, if either operand is double, the other shall be converted to double.
— Otherwise, if either operand is float, the other shall be converted to float.
— Otherwise, the integral promotions (4.5) shall be performed on both operands.54)
Then, if either operand is unsigned long the other shall be converted to unsigned long.
— Otherwise, if one operand is a long int and the other unsigned int, then if a long int can represent
all the values of an unsigned int, the unsigned int shall be converted to a long int;
otherwise both operands shall be converted to unsigned long int.
— Otherwise, if either operand is long, the other shall be converted to long.
— Otherwise, if either operand is unsigned, the other shall be converted to unsigned.
[Note: otherwise, the only remaining case is that both operands are int ]

Любите книгу — источник знаний (с) М.Горький
Re[4]: Глюк или фича?
От: 7C  
Дата: 13.06.07 08:42
Оценка:
Здравствуйте, Bell, Вы писали:

B>Это неверно. Тип выражения определяется типом "максимального" операнда, и при этом совершенно неважно, на каком месте в выражении он находится.


B>

B>5/9
B>Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield
B>result types in a similar way. The purpose is to yield a common type, which is also the type of the result.
B>This pattern is called the usual arithmetic conversions, which are defined as follows:
B>— If either operand is of type long double, the other shall be converted to long double.
B>— Otherwise, if either operand is double, the other shall be converted to double.
B>— Otherwise, if either operand is float, the other shall be converted to float.
B>— Otherwise, the integral promotions (4.5) shall be performed on both operands.54)
B>— Then, if either operand is unsigned long the other shall be converted to unsigned long.
B>— Otherwise, if one operand is a long int and the other unsigned int, then if a long int can represent
B>all the values of an unsigned int, the unsigned int shall be converted to a long int;
B>otherwise both operands shall be converted to unsigned long int.
B>— Otherwise, if either operand is long, the other shall be converted to long.
B>— Otherwise, if either operand is unsigned, the other shall be converted to unsigned.
B>[Note: otherwise, the only remaining case is that both operands are int ]


Вах. Тогда все понятно.
Век живи...
Re[3]: Глюк или фича?
От: Seon  
Дата: 13.06.07 08:46
Оценка:
Здравствуйте, Нестеренко Сергей, Вы писали:

S>>Переставь а и z местами.

НС>Не выйдет. Нужно явно привести типы. long b = (int)(a*z)/1000;

Та знаю, Это я Бориса приколол.
Re[4]: Глюк или фича?
От: Roman Odaisky Украина  
Дата: 13.06.07 14:21
Оценка:
Здравствуйте, Seon, Вы писали:

S>>>Переставь а и z местами.

НС>>Не выйдет. Нужно явно привести типы. long b = (int)(a*z)/1000;

S>Та знаю, Это я Бориса приколол.


Всё равно не так!

long b = int(a) * int(z) / 1000;
До последнего не верил в пирамиду Лебедева.
Re[5]: Глюк или фича?
От: 7C  
Дата: 14.06.07 04:04
Оценка:
7C>Здравствуйте, Bell, Вы писали:

B>>Это неверно. Тип выражения определяется типом "максимального" операнда, и при этом совершенно неважно, на каком месте в выражении он находится.


B>>

B>>5/9
B>>Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield
B>>result types in a similar way. The purpose is to yield a common type, which is also the type of the result.
B>>This pattern is called the usual arithmetic conversions, which are defined as follows:
B>>— If either operand is of type long double, the other shall be converted to long double.
B>>— Otherwise, if either operand is double, the other shall be converted to double.
B>>— Otherwise, if either operand is float, the other shall be converted to float.
B>>— Otherwise, the integral promotions (4.5) shall be performed on both operands.54)
B>>— Then, if either operand is unsigned long the other shall be converted to unsigned long.
B>>— Otherwise, if one operand is a long int and the other unsigned int, then if a long int can represent
B>>all the values of an unsigned int, the unsigned int shall be converted to a long int;
B>>otherwise both operands shall be converted to unsigned long int.
B>>— Otherwise, if either operand is long, the other shall be converted to long.
B>>— Otherwise, if either operand is unsigned, the other shall be converted to unsigned.
B>>[Note: otherwise, the only remaining case is that both operands are int ]


Нет, все равно не понимаю!
Тут должно работать следующее правило (выделенное):
Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield
result types in a similar way. The purpose is to yield a common type, which is also the type of the result.
This pattern is called the usual arithmetic conversions, which are defined as follows:
— If either operand is of type long double, the other shall be converted to long double.
— Otherwise, if either operand is double, the other shall be converted to double.
— Otherwise, if either operand is float, the other shall be converted to float.
— Otherwise, the integral promotions (4.5) shall be performed on both operands.54)
— Then, if either operand is unsigned long the other shall be converted to unsigned long.
— Otherwise, if one operand is a long int and the other unsigned int, then if a long int can represent
all the values of an unsigned int, the unsigned int shall be converted to a long int;
otherwise both operands shall be converted to unsigned long int.
— Otherwise, if either operand is long, the other shall be converted to long.
— Otherwise, if either operand is unsigned, the other shall be converted to unsigned.
[Note: otherwise, the only remaining case is that both operands are int ]

Честно говоря, это правило непонятно для меня (точнее непонятна его двойственность).
Ну да бог с ним.
Но это правило мне не объясняет почему по-разному обрабатываются строки:
long b = a*z/1000; //b=4294261
long c = a*z; // c=-705600


P.S. Явное приведение типов [(int)x или int(x)] меня мало интересует (это всё я знаю). Меня смущает неожиданное и неопределенное поведение компилятора.
Re[6]: Глюк или фича?
От: Max M. Ниоткуда  
Дата: 14.06.07 06:04
Оценка:
Здравствуйте, 7C, Вы писали:

7C>Но это правило мне не объясняет почему по-разному обрабатываются строки:

7C>
7C>long b = a*z/1000; //b=4294261
7C>long c = a*z; // c=-705600
7C>


kankan в первом же ответе четко расписал:

//b — это преобразованный к long результат деления беззнакового a*z на 1000
// то есть 0xfff53bc0 сначала делится на 1000, а потом кастится к знаковому
long b = a*z/1000; //b=4294261

//здесь беззнаковое a*z (0xfff53bc0) сначала кастится к long(-705600), а потом аж делится на тыщу
long c = a*z; // c=-705600
long d = c/1000; // d =-705


7C>Меня смущает неожиданное и неопределенное поведение компилятора.


именно что ожиданное и вполне определенное.
// ...
подпись
Re[6]: Глюк или фича?
От: Bell Россия  
Дата: 14.06.07 08:53
Оценка:
Здравствуйте, 7C, Вы писали:

7C>Нет, все равно не понимаю!

7C>Тут должно работать следующее правило (выделенное):
7C>
7C>— Otherwise, if one operand is a long int and the other unsigned int, then if a long int can represent
7C>all the values of an unsigned int, the unsigned int shall be converted to a long int;
7C>otherwise both operands shall be converted to unsigned long int.
7C>

7C>Честно говоря, это правило непонятно для меня (точнее непонятна его двойственность).
7C>Ну да бог с ним.

В нашем случае long int не может "вместить" unsigned int (т.к. и int и long по 4 байти), поэтому все операнды преобразуются в unsigned long.
7C>Но это правило мне не объясняет почему по-разному обрабатываются строки:
7C>
7C>long b = a*z/1000; //b=4294261
7C>long c = a*z; // c=-705600
7C>


7C>P.S. Явное приведение типов [(int)x или int(x)] меня мало интересует (это всё я знаю). Меня смущает неожиданное и неопределенное поведение компилятора.


Мне кажется, что тебя сбивает с толку тот факт, что выражение
long c = a*z; // c=-705600

дает ожидаемый результат.

Попробуй записать все это вот так:
unsigned int z = 176400;
long a = -4;
double dd = (unsigned long)a * (unsigned long)z;
long d = (long)dd;


ну или даже вот так:

unsigned int z = 176400;
long a = -4;
unsigned long ua = (unsigned long)a;
double dd = ua * (unsigned long)z;
long d = (long)dd;
Любите книгу — источник знаний (с) М.Горький
Re: Глюк или фича?
От: Кодт Россия  
Дата: 14.06.07 10:34
Оценка:
Здравствуйте, 7C, Вы писали:

7C>Наткнулся на неожиданное поведение вычислений в программе:


Всё ожиданно, если ты напишешь типы выражений и подвыражений.
1) Когда в операции участвуют знаковый и беззнаковый аргументы, то знаковый приводится к соответствующему беззнаковому
2) Затем аргументы приводятся к максимальному из участвующих типов
Поэтому long*uint -> ulong*uint -> ulong*ulong

(Это я так, на пальцах — подробнее см. стандарт, 4.7)

Смотрим, что вышло по дефолту
7C>int main()
7C>{
7C>    unsigned int z = 176400;
7C>    long a = -4;
7C>    long b = long(ulong(a)*ulong(z)/ulong(1000)); //b=4294261 = 0xFFFFFFFB * 176400 / 1000
7C>    long c = long(ulong(a)*ulong(z)); // c=-705600
7C>    long d = c/long(1000); // d =-705
7C>    return 0;
7C>}

Т.е.
ulong uaz = a*z; // нечто, безусловно неотрицательное
long  saz = uaz; // поскольку uaz > LONG_MAX, т.е. его старший бит взведён, получили отрицательное число

long b = uaz/1000; // деление на 1000 обеспечивает попадание внутрь диапазона 0..LONG_MAX
long d = saz/1000; // деление отрицательного числа - получили отрицательное
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re: Глюк или фича?
От: icWasya  
Дата: 15.06.07 08:35
Оценка:
Здравствуйте, 7C, Вы писали:

7C>
7C>int main()
7C>{
7C>    unsigned int z = 176400;
7C>    long a = -4;
7C>    long b = a*z/1000; //b=4294261
7C>    long c = a*z; // c=-705600
7C>    long d = c/1000; // d =-705
7C>    return 0;
7C>}
7C>

7C>Компилятор Visual C++ 2005

Щас проверил на Borland C++ 3.0
Результаты те же.
В коде оба умножения делаются знаковые.
Первое деление беззнаковое, второе знаковое.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.