Сообщение Re[8]: Carbon от 19.04.2024 11:15
Изменено 19.04.2024 11:20 vdimas
Re[8]: Carbon
Здравствуйте, Sinclair, Вы писали:
V>>В данном случае ты даёшь оценку своим фантазиям, бо архитектуры/компиляторы явно определяют своё поведение при переполнении.
S>Ошибаетесь. Я же привёл пример. Каким образом "явно определённое поведение" приводит к разнице между -O0 и -O2 для приведённого фрагмента?
1. Твой пример содержит ошибки, например, для short, т.к. значением short(value)+1 будет значение типа int, т.е. еще до рассуждений об UB необходимо исправить ошибку:
S>Что мы и наблюдаем в приведённом примере.
2. Мы наблюдаем невладение предметом, бо в C/C++ еще есть битовые поля, для которых это однозначное UB со всеми спецификациями платформы:
V>>Т.е. можно подобрать такое кодирование, что в твоём коде будет UB именно для этой платформы, например, для обратного кодирования есть два нуля для знаковых чисел — 0000 и 1111, где при сравнении первый ноль меньше второго, и твоя программа закономерно поломалась, как и предостерегал стандарт.
S> Нет. Не угадали.
фуф, тяжело с тобой...
Такое ощущение, что тебе всю жизнь пришлось работать среди откровенно тупых людей. ))
Мои соболезнования, кстате.
Но что в любом обсуждении ты теперь на рефлексах считаешь собеседников по-умолчанию идиотами, и даже не мелькает мысли проверить себя — это изрядно утомляет, конечно...
S>Программа прекрасно ломается на совершенно любой платформе.
Программа ломается, потому что в ней ошибки. ))
Ты написал некий обощённый код из некоторого обощённого предположения, но это предположение для многих ситуаций ложно.
3. В общем, если уж охота поупражняться с битами без углубления в подробности, то это всегда делается на беззнаковых.
А если оперируешь знаковыми — то изволь максимально погрузиться в подробности и отвечай за каждый бит, как грится.
Смотри, ты сделал предположение, что старший бит у отрицательного числа на всех известных (и даже экзотических) платформах будет равен 1 и этот бит возникнет при переполнении.
И это было верное предположение.
А далее ты, вместо того, чтобы написать программу согласно предположению, т.е. тупо проверить старший бит, зачем-то стал возиться со сложением, не обращая внимания на потенциальное битовое расположение реальных данных в памяти (битовые поля) или в регистрах.
Фишка в том, что для промежуточных вычислений в железе часто используют регистры, шириной в слово, т.е. UB возникает прямо в железе — ты можешь проверить это на последних gcc на x64, там будет тот же эффект в рантайм для int, что и в случае short(value)+1, т.е. даже в рантайме программа сломается — потому что в ней ошибка — ты не можешь гарантировать ширину бит вычислений.
А ведь достаточно наложить маску 0xFFFF(FF) требуемой ширины — и код не сломается на любых флагах.
(хочешь
Тут в максимальной оптимизации, когда до реальных вычислений is_max в рантайме не доходит, то compile-time вычисления всё-равно показывают ожидаемый тобой результат.
(а ваше это обсуждение с коллегой было забавным конечно, я потом ниже почитал — вы в упор не увидели сразу 2 ошибки)
V>>В данном случае ты даёшь оценку своим фантазиям, бо архитектуры/компиляторы явно определяют своё поведение при переполнении.
S>Ошибаетесь. Я же привёл пример. Каким образом "явно определённое поведение" приводит к разнице между -O0 и -O2 для приведённого фрагмента?
1. Твой пример содержит ошибки, например, для short, т.к. значением short(value)+1 будет значение типа int, т.е. еще до рассуждений об UB необходимо исправить ошибку:
template<typename signed_integral>
bool is_max(signed_integral value) {
return signed_integral(value + 1) < value;
}
S>Что мы и наблюдаем в приведённом примере.
2. Мы наблюдаем невладение предметом, бо в C/C++ еще есть битовые поля, для которых это однозначное UB со всеми спецификациями платформы:
int main()
{
struct S {
int field1 : 8,
field2 : 7,
flag : 1;
} s = { 127, 63, 1 };
std::cout
<< (is_max(s.field1) ? "wow " : "oops ")
<< (is_max(s.field2) ? "wow " : "oops ")
<< (is_max(s.flag) ? "wow " : "oops ")
<< std::endl;
using sbyte = signed char;
std::cout
<< (is_max(static_cast<sbyte>(127)) ? "wow " : "oops ")
<< (is_max(static_cast<short>(32767)) ? "wow " : "oops ")
<< std::endl;
return 0;
}
V>>Т.е. можно подобрать такое кодирование, что в твоём коде будет UB именно для этой платформы, например, для обратного кодирования есть два нуля для знаковых чисел — 0000 и 1111, где при сравнении первый ноль меньше второго, и твоя программа закономерно поломалась, как и предостерегал стандарт.
S> Нет. Не угадали.
фуф, тяжело с тобой...
Такое ощущение, что тебе всю жизнь пришлось работать среди откровенно тупых людей. ))
Мои соболезнования, кстате.
Но что в любом обсуждении ты теперь на рефлексах считаешь собеседников по-умолчанию идиотами, и даже не мелькает мысли проверить себя — это изрядно утомляет, конечно...
S>Программа прекрасно ломается на совершенно любой платформе.
Программа ломается, потому что в ней ошибки. ))
Ты написал некий обощённый код из некоторого обощённого предположения, но это предположение для многих ситуаций ложно.
3. В общем, если уж охота поупражняться с битами без углубления в подробности, то это всегда делается на беззнаковых.
А если оперируешь знаковыми — то изволь максимально погрузиться в подробности и отвечай за каждый бит, как грится.
Смотри, ты сделал предположение, что старший бит у отрицательного числа на всех известных (и даже экзотических) платформах будет равен 1 и этот бит возникнет при переполнении.
И это было верное предположение.
А далее ты, вместо того, чтобы написать программу согласно предположению, т.е. тупо проверить старший бит, зачем-то стал возиться со сложением, не обращая внимания на потенциальное битовое расположение реальных данных в памяти (битовые поля) или в регистрах.
Фишка в том, что для промежуточных вычислений в железе часто используют регистры, шириной в слово, т.е. UB возникает прямо в железе — ты можешь проверить это на последних gcc на x64, там будет тот же эффект в рантайм для int, что и в случае short(value)+1, т.е. даже в рантайме программа сломается — потому что в ней ошибка — ты не можешь гарантировать ширину бит вычислений.
А ведь достаточно наложить маску 0xFFFF(FF) требуемой ширины — и код не сломается на любых флагах.
(хочешь
#include <iostream>
#include <limits.h>
template<typename signed_integral>
struct mask;
template<>
struct mask<signed char> {
static const signed char value = 0xFF;
};
template<>
struct mask<short> {
static const short value = 0xFFFF;
};
template<>
struct mask<int> {
static const int value = 0xFFFFFFFF;
};
template<>
struct mask<long long> {
static const long long value = 0xFFFFFFFFFFFFFFFFL;
};
template<typename signed_integral>
bool is_max(signed_integral value) {
return (static_cast<signed_integral>(value+1) && mask<signed_integral>::value) < value;
}
int main()
{
std::cout << (is_max(static_cast<signed char>(127))? "wow" : "oops" ) << std::endl;
std::cout << (is_max(SHRT_MAX)? "wow" : "oops" ) << std::endl;
std::cout << (is_max(INT_MAX)? "wow" : "oops" ) << std::endl;
std::cout << (is_max(static_cast<long long>(0x7FFFFFFFFFFFFFFFL))? "wow" : "oops" ) << std::endl;
return 0;
}
Тут в максимальной оптимизации, когда до реальных вычислений is_max в рантайме не доходит, то compile-time вычисления всё-равно показывают ожидаемый тобой результат.
(а ваше это обсуждение с коллегой было забавным конечно, я потом ниже почитал — вы в упор не увидели сразу 2 ошибки)
Re[8]: Carbon
Здравствуйте, Sinclair, Вы писали:
V>>В данном случае ты даёшь оценку своим фантазиям, бо архитектуры/компиляторы явно определяют своё поведение при переполнении.
S>Ошибаетесь. Я же привёл пример. Каким образом "явно определённое поведение" приводит к разнице между -O0 и -O2 для приведённого фрагмента?
1. Твой пример содержит ошибки, например, для short, т.к. значением short(value)+1 будет значение типа int, т.е. еще до рассуждений об UB необходимо исправить ошибку:
S>Что мы и наблюдаем в приведённом примере.
2. Мы наблюдаем невладение предметом, бо в C/C++ еще есть битовые поля, для которых это однозначное UB со всеми спецификациями платформы:
V>>Т.е. можно подобрать такое кодирование, что в твоём коде будет UB именно для этой платформы, например, для обратного кодирования есть два нуля для знаковых чисел — 0000 и 1111, где при сравнении первый ноль меньше второго, и твоя программа закономерно поломалась, как и предостерегал стандарт.
S> Нет. Не угадали.
фуф, тяжело с тобой...
Такое ощущение, что тебе всю жизнь пришлось работать среди откровенно тупых людей. ))
Мои соболезнования, кстате.
Но что в любом обсуждении ты теперь на рефлексах считаешь собеседников по-умолчанию идиотами, и даже не мелькает мысли проверить себя — это изрядно утомляет, конечно...
S>Программа прекрасно ломается на совершенно любой платформе.
Программа ломается, потому что в ней ошибки. ))
Ты написал некий обощённый код из некоторого обощённого предположения, но это предположение для многих ситуаций ложно.
3. В общем, если уж охота поупражняться с битами без углубления в подробности, то это всегда делается на беззнаковых.
А если оперируешь знаковыми — то изволь максимально погрузиться в подробности и отвечай за каждый бит, как грится.
Смотри, ты сделал предположение, что старший бит у отрицательного числа на всех известных (и даже экзотических) платформах будет равен 1 и этот бит возникнет при переполнении.
И это было верное предположение.
А далее ты, вместо того, чтобы написать программу согласно предположению, т.е. тупо проверить старший бит, зачем-то стал возиться со сложением, не обращая внимания на потенциальное битовое расположение реальных данных в памяти (битовые поля) или в регистрах.
Фишка в том, что для промежуточных вычислений в железе часто используют регистры шириной в слово, т.е. UB возникает прямо в железе — ты можешь проверить это на последних gcc на x64, там будет тот же эффект в рантайм для int, что и в случае short(value)+1, т.е. даже в рантайме программа сломается, потому что в ней ошибка — ты не можешь гарантировать ширину бит промежуточных вычислений.
А ведь достаточно наложить маску 0xFFFF(FF) требуемой ширины — и код не сломается на любых флагах.
(хочешь
Тут в максимальной оптимизации, когда до реальных вычислений is_max в рантайме не доходит, то compile-time вычисления всё-равно показывают ожидаемый тобой результат.
(а ваше это обсуждение с коллегой было забавным конечно, я потом ниже почитал — вы в упор не увидели сразу 2 ошибки)
V>>В данном случае ты даёшь оценку своим фантазиям, бо архитектуры/компиляторы явно определяют своё поведение при переполнении.
S>Ошибаетесь. Я же привёл пример. Каким образом "явно определённое поведение" приводит к разнице между -O0 и -O2 для приведённого фрагмента?
1. Твой пример содержит ошибки, например, для short, т.к. значением short(value)+1 будет значение типа int, т.е. еще до рассуждений об UB необходимо исправить ошибку:
template<typename signed_integral>
bool is_max(signed_integral value) {
return signed_integral(value + 1) < value;
}
S>Что мы и наблюдаем в приведённом примере.
2. Мы наблюдаем невладение предметом, бо в C/C++ еще есть битовые поля, для которых это однозначное UB со всеми спецификациями платформы:
int main()
{
struct S {
int field1 : 8,
field2 : 7,
flag : 1;
} s = { 127, 63, 1 };
std::cout
<< (is_max(s.field1) ? "wow " : "oops ")
<< (is_max(s.field2) ? "wow " : "oops ")
<< (is_max(s.flag) ? "wow " : "oops ")
<< std::endl;
using sbyte = signed char;
std::cout
<< (is_max(static_cast<sbyte>(127)) ? "wow " : "oops ")
<< (is_max(static_cast<short>(32767)) ? "wow " : "oops ")
<< std::endl;
return 0;
}
V>>Т.е. можно подобрать такое кодирование, что в твоём коде будет UB именно для этой платформы, например, для обратного кодирования есть два нуля для знаковых чисел — 0000 и 1111, где при сравнении первый ноль меньше второго, и твоя программа закономерно поломалась, как и предостерегал стандарт.
S> Нет. Не угадали.
фуф, тяжело с тобой...
Такое ощущение, что тебе всю жизнь пришлось работать среди откровенно тупых людей. ))
Мои соболезнования, кстате.
Но что в любом обсуждении ты теперь на рефлексах считаешь собеседников по-умолчанию идиотами, и даже не мелькает мысли проверить себя — это изрядно утомляет, конечно...
S>Программа прекрасно ломается на совершенно любой платформе.
Программа ломается, потому что в ней ошибки. ))
Ты написал некий обощённый код из некоторого обощённого предположения, но это предположение для многих ситуаций ложно.
3. В общем, если уж охота поупражняться с битами без углубления в подробности, то это всегда делается на беззнаковых.
А если оперируешь знаковыми — то изволь максимально погрузиться в подробности и отвечай за каждый бит, как грится.
Смотри, ты сделал предположение, что старший бит у отрицательного числа на всех известных (и даже экзотических) платформах будет равен 1 и этот бит возникнет при переполнении.
И это было верное предположение.
А далее ты, вместо того, чтобы написать программу согласно предположению, т.е. тупо проверить старший бит, зачем-то стал возиться со сложением, не обращая внимания на потенциальное битовое расположение реальных данных в памяти (битовые поля) или в регистрах.
Фишка в том, что для промежуточных вычислений в железе часто используют регистры шириной в слово, т.е. UB возникает прямо в железе — ты можешь проверить это на последних gcc на x64, там будет тот же эффект в рантайм для int, что и в случае short(value)+1, т.е. даже в рантайме программа сломается, потому что в ней ошибка — ты не можешь гарантировать ширину бит промежуточных вычислений.
А ведь достаточно наложить маску 0xFFFF(FF) требуемой ширины — и код не сломается на любых флагах.
(хочешь
#include <iostream>
#include <limits.h>
template<typename signed_integral>
struct mask;
template<>
struct mask<signed char> {
static const signed char value = 0xFF;
};
template<>
struct mask<short> {
static const short value = 0xFFFF;
};
template<>
struct mask<int> {
static const int value = 0xFFFFFFFF;
};
template<>
struct mask<long long> {
static const long long value = 0xFFFFFFFFFFFFFFFFL;
};
template<typename signed_integral>
bool is_max(signed_integral value) {
return (static_cast<signed_integral>(value+1) && mask<signed_integral>::value) < value;
}
int main()
{
std::cout << (is_max(static_cast<signed char>(127))? "wow" : "oops" ) << std::endl;
std::cout << (is_max(SHRT_MAX)? "wow" : "oops" ) << std::endl;
std::cout << (is_max(INT_MAX)? "wow" : "oops" ) << std::endl;
std::cout << (is_max(static_cast<long long>(0x7FFFFFFFFFFFFFFFL))? "wow" : "oops" ) << std::endl;
return 0;
}
Тут в максимальной оптимизации, когда до реальных вычислений is_max в рантайме не доходит, то compile-time вычисления всё-равно показывают ожидаемый тобой результат.
(а ваше это обсуждение с коллегой было забавным конечно, я потом ниже почитал — вы в упор не увидели сразу 2 ошибки)