Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524

[c++] упростить конструкцию safe_cast, преобразование типов без потерь данных

10.06.2020, 23:22. Показов 2309. Ответов 28

Студворк — интернет-сервис помощи студентам
Всем привет!

Сборка проекта на максимальных уровнях предупреждений очень часто терпит неудачу из-за предупреждений вида:
Code
1
error: conversion to 'DWORD' {aka 'long unsigned int'} from 'int' may change the sign of the result [-Werror=sign-conversion]

И как то не хочется тратить кучу времени на разбирательства с каждым таким случаем.
Хочется просто сразу вписать static_cast, в расчете на то, что даже если источник данных имеет больший размер,
чем получатель, все равно сами данные всегда будут в пределах диапазона получателя.

А на случай нарушения этого джентельменского соглашения хочется иметь дополнительную страховку.

Следующий код иллюстрирует сабж:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <type_traits>
#include <stdexcept>
#include <iostream>
#include <limits>
 
template<class ret, class from>
ret safe_cast(from&& val)
{
    using x = decltype( ::std::numeric_limits<ret>::max() );
    using y = ::std::remove_reference_t<from>;
    using z = ::std::common_type_t<x, y>;
    const auto& check = static_cast<z>(val);
    if(::std::numeric_limits<ret>::max() < check)
        throw ::std::logic_error("data loss conversion");
    return static_cast<ret>(val);
}
 
 
int main()
{
    try
    {
        size_t val = size_t(-10);
 
        int v = safe_cast<int>(val);
 
        std::cout << "v = " << v << '\n';
    }
    catch(const ::std::exception& e)
    {
        std::cerr << e.what() << '\n';
    }
}
Этот способ работает, но не мне нравится зависимость от limits
Не нравится зависимость от type_traits и decltype

Мне кажется можно сделать как то попроще.
Как то такие вещи же делали во времена с++03, когда decltype ещё не существовало?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
10.06.2020, 23:22
Ответы с готовыми решениями:

Как работает safe_cast и чем отличается от обычного приведения типов?
Объясните пожалуйста как работает операция safe_cast. Максимально подробно, именно на низком уровне. То что на msdn-не хватает. ...

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

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

28
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13239 / 6871 / 1827
Регистрация: 18.10.2014
Сообщений: 17,416
11.06.2020, 16:50
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от eva2326 Посмотреть сообщение
Задача заключается в том, что бы избавиться от предупреждений.
Предупреждения - компиляторно- зависимое свойство. "Избавиться от предупреждений" можно только задавив их компиляторно-зависимыми средствами, как на уровне кода, так и на уровне настроек компилятора.

Цитата Сообщение от eva2326 Посмотреть сообщение
Здесь написано, что касты int32_t <=> uint32_t совершенно безопасны.
Так как размер у типов одинаковый, то содержимое памяти при касте останется неизменным.
Ым.. Касты в С++ ни имеют никакого отношения к "содержимому памяти". Касты в С++ всегда работают только на уровне высокоуровневых/логических представлений значения. "Содержимое памяти" тут вообще впрямую не фигурирует.

Если вас интересует "содержимое памяти", то вам нужна реинтерпретация, а не касты. Реинтерпретацию делают memcpy, union и доcтуп через unsigned char * указатель.

Цитата Сообщение от eva2326 Посмотреть сообщение
Каст float <=> int32_t, при условии что sizeof(float) == 32 тоже безопасен?
В свете вышесказанного вами, это вопрос становится бессмысленным. О чем идет речь? О "содержимом памяти" (реинтерпретации) или все таки о касте (приведении типа)?

Цитата Сообщение от eva2326 Посмотреть сообщение
Проверку значения нужно делать только в случае, если кастим из объекта большего размера в объект меньшего размера?
Еще раз: тема кастов и потери (или не потери) информации при касте не имеет вообще никакого отношения к размерам объектов.

Цитата Сообщение от eva2326 Посмотреть сообщение
Но я имела ввиду с точки зрения реинтерпретации данных.
Ах, к реинтерпретации... К чему тогда здесь этот код, который вы привели в исходном сообщении и который к реинтерпретации не имеет даже отдаленного отношения?

С реинтерпретацией данных все просто: байтов хватает - значит данные поместятся. Сравните sizeof, сделайте memcpy - и готово. Однако помните, что может получиться trap representation. Т.е. пытаться работать с float значением, полученным путем реинтерпретации int значения - это дикий бред и феерическая чушь (и да, я помню про трюк из doom).

---

Перечитал ваше исходное сообщение. Вердикт: и по процитированному предупреждению компилятора, и по приведенному коду очень хорошо видно, что ни о какой "реинтерпретации" речи в вашем вопросе не идет. Ваш вопрос - именно о приведении типа, т.е. о кастах. Этот вопрос я закрыл дал в сообщении #3. Зачем вы сейчас бросились выдумывать какую-то "реинтерпретацию" - тему, к который ваш изначальный вопрос не имел никакого отношения - мне не ясно.
0
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
11.06.2020, 16:55  [ТС]
Можно ли записать double во float без потерь?

Первый эксперимент:
Берем float, и засовываем его в double.
А потом вытаскиваем обратно из double во float.
Если получившееся значения пренебрежительно равно исходному, значит данное значение float можно без потерь поместить в double

Для тестирования использовала фреймворк gtest

C++
1
2
3
4
5
6
7
TEST_COMPONENT(001)
{
    const float  f1 = 13.13f;
    const double d  = f1;
    const float  f2 = d;
    ASSERT_FLOAT_EQ(f1, f2);
}
Два дробных считаются равны, если их значения отличаются очень не значительно.
Подробности в исходном коде gtest.

Кликните здесь для просмотра всего текста

у них там UB, кстати, в классе FloatingPoint,
но всем пофигу.


Результат: float можно записать в double без потерь данных.

Второй экперимент:
Берем double, и засовываем его во float.
А потом вытаскиваем обратно.
Если получившееся значения слишком сильно разнятся, значит нельзя сохранить double во float, без потерь данных

C++
1
2
3
4
5
6
7
TEST_COMPONENT(002)
{
    const double d1 = 13.13;
    const float  f  = static_cast<float>(d1);
    const double d2 = f;
    ASSERT_DOUBLE_EQ(d1, d2);
}
Тест провалился.

Отладчик показывает:

Code
1
2
3
d1  13.130000000000001
d2  13.130000114440918
f   13.1300001
Каст из float в double приводит к возникновению артефактов.

То есть по сути, каст из float в double приводит к искажению данных.
Что на мой взгляд, ещё хуже чем потеря данных.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13239 / 6871 / 1827
Регистрация: 18.10.2014
Сообщений: 17,416
11.06.2020, 17:11
Цитата Сообщение от eva2326 Посмотреть сообщение
Каст из float в double приводит к возникновению артефактов.
То есть по сути, каст из float в double приводит к искажению данных.
Написана какая-то ерунда.

Каст из double в float приводит к искажению значения. Что ожидаемо.

Каст из float в double никогда не приводит к потере значения, по крайней мере в IEEE754 представлениях.

Все искажения в ваших экспериментах возникли только при касте из double в float, а не наоборот.

Цитата Сообщение от eva2326 Посмотреть сообщение
искажению данных.
Что на мой взгляд, ещё хуже чем потеря данных.
Это где, интересно, вы проводите водораздел между "искажением" и "потерей"???
2
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
11.06.2020, 17:29  [ТС]
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
"Избавиться от предупреждений" можно только задавив их компиляторно-зависимыми средствами, как на уровне кода, так и на уровне настроек компилятора.
Нет, не только.
Ещё можно просто писать нормальный код, который не агрит компиляторы.

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Если вас интересует "содержимое памяти", то вам нужна реинтерпретация, а не касты.
Меня интересуют данные, которые не должны испортиться.
И компиляторы, которые не должны кидать предупреждений.

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Этот вопрос я закрыл дал в сообщении #3
Код, представленный в #3 делает не то, что нужно.
И к тому же, возбуждает у компиляторов предупреждение.

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

Вы отдаёте себе отчет в том, что запостили такой код в тему, которая посвящена тому, как можно избавиться от подобных предупреждений?

Не находите, что это немножко глупо?

В этой теме я вообще не буду рассматривать код, который агрит компиляторы.

Добавлено через 3 минуты
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Каст из double в float приводит к искажению значения.
Я это и хотела написать. Я перепутала ))))

Ну видно же по эксперименту?
Цитата Сообщение от eva2326 Посмотреть сообщение
const float  f  = static_cast<float>(d1)
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13239 / 6871 / 1827
Регистрация: 18.10.2014
Сообщений: 17,416
11.06.2020, 17:45
Цитата Сообщение от eva2326 Посмотреть сообщение
Ещё можно просто писать нормальный код, который не агрит компиляторы.
Нет, конечно. Во-первых, "нормальный код", о которым вы говорите - это и есть методы подавления предупреждений на уровне исходного кода, о которых я говорил выше. И которые, разумеется, по определению компиляторно-зависимы.

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

Цитата Сообщение от eva2326 Посмотреть сообщение
Меня интересуют данные, которые не должны испортиться.
Это при том, что вы до сих пор не определились, что значит "данные испортились". И при каких манипуляциях они должны (или не должны) портиться. Вы начали с кастов, потом вдруг забыли про касты и переключились не реинретпретацию памяти, потом снова забыли про реинретпретацию памяти и переключились на "круговой каст".

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

Цитата Сообщение от eva2326 Посмотреть сообщение
Вы отдаёте себе отчет в том, что запостили такой код в тему, которая посвящена тому, как можно избавиться от подобных предупреждений?
Еще раз: вы пока что еще сами не определились, чему посвящена эта тема, как я объяснил выше. На данный момент, я знаю чему посвящена ваша тема лучше вас. Так понятнее?

Цитата Сообщение от eva2326 Посмотреть сообщение
В этой теме я вообще не буду рассматривать код, который агрит компиляторы.
Боюсь что наличие в лексиконе терминов вроде "агрит" также является существенным препятствием к пониманию.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
11.06.2020, 17:58
Цитата Сообщение от eva2326 Посмотреть сообщение
Если получившееся значения пренебрежительно равно исходному,
eva2326, это не о реинтерпретации. Это именно о преобразовании значения. А дальше вступает математика. Я не большой математик, но перевод вашего:
Цитата Сообщение от eva2326 Посмотреть сообщение
пренебрежительно равно
применительно к мантиссам разной битности (бог с ним с показателем - он целый и тут легко представить что double можно ограничить максимумом для float) приводит к ограничению для double состоящему в странном требовании:
биты не задействованные во флоат должны быть нулевыми.
Знак не учитываю и формат считаю семантически подобным.
Это учитывая факт двоичного представления - задача пикантная. Не уверен в том, что есть нетривиальный способ это проконтролировать. Хотя написать платформеннозависимую функцию выполняющую битовый анализ и возвращающую даже ошибку округления (или невозможность представления) впринципе возможно. Но это далеко от возможностей преобразования типов языка С++. Может и писал кто-то что-то подобное.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13239 / 6871 / 1827
Регистрация: 18.10.2014
Сообщений: 17,416
11.06.2020, 17:58
del
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
11.06.2020, 20:15
Для конкретно плавающих типов можно lexical_cast из boost применить. Говорить о скорости тут вряд ли придётся.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
12.06.2020, 09:42
Вот ещё материал, но он уже содержит пыль веков. Компиляторы стали умнее. Однако, почитать может быть интересно:
Андрей Александреску Современное проектирование на С++
гл.2.1. Статическая проверка диагностических утверждений.
Тут о безопасности статических преобразований. По сути, как было сказано, компилятор и сам не даст данным умереть спокойно (сужающий вариант), но если хочется расширенной диагностической информации в то время как сообщение компилятора не устраивает, можно написать свои шаблоны.
гл.2.7. Распознавание конвертируемости и наследования на этапе компиляции
Это о возможности преобразований типов иерархии (указателей вернее). Разумеется, всё относится ко времени компиляции и ни как не конкурирует с RTTI.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
12.06.2020, 09:42

Преобразование типов без исчезания нуля
ребят помогите с проблемой ... есть у меня label в нем написано число 01101 я это число хочу преобразовать в инт что бы в дальнейшем...

Упростить конструкцию
Возможно сделать ли проще код? $.each($(''), function(index, vl){ $(this).on('click', function(elem) { atr =...

упростить конструкцию
for (int i = 0, c_c = computer_player, c_h = human_player, r; i &lt; human_player + computer_player; i++) { r = rand() % 2; string...

Упростить конструкцию
Изначально: 22,33,44,66,55 $arrr = array_flip(explode(',',$gl)); // Создаем массив, затем меняем кличи со значением unset($arrr);...

Как упростить конструкцию?
Добрый всем вечер. Подскажите как можно упростить/сократить такую конструкцию: label7.Text = Array_Res_Level.ToString(); ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
29
Ответ Создать тему
Опции темы

Новые блоги и статьи
Про "Windows must die!"
kumehtar 04.11.2026
Знаете, чет накатило. . . . Когда-то это было почти боевым кличем: мол, нормальный человек сидит на DOS, OS/ 2 или Linux, а вот это вот - Must Die. А теперь человек произносит «мастдай» с усмешкой - и. . .
Красочные поля с номером страницы svg+background-image
russiannick 04.11.2026
Красочные поля с номером страницы не менее важны чем в эпоху бумажных книг. Посему спешу представить почтеннейшей публике этот важный инструмент который эти поля и формирует. В процессе разработки. . .
Вступаем в новую фазу генерации favicons, векторную.
russiannick 02.11.2026
Теперь нет нужды скрывать что используется простой svg. Исходный код изображения помещаем в поле и жмем кнопку. Исходник преобразуется в вид корректный для помещения в favicon или в url(). Нажатие. . .
Генератор паролей (вариант №4)
Maks 01.11.2026
После публикации 3-го варианта генератора паролей, разослал его своим коллегам (с целью оценки), в итоге были получены следующие рекомендации: 1. Вернуть возможность использования знаков | 1 l I;. . .
Генератор паролей (вариант №3)
Maks 30.10.2026
Финальная версия генератора паролей 1С. Доработка решения в этот раз коснулась только визуальной части, а именно, добавлен реквизит ДопПараметры (тип Булево), который скрывает/ отображает реквизиты:. . .
Магическое мышление и магия, современная наука не отличима от магии (говорит ИИ Джемени)
zorxor 30.10.2026
Джемени, расскажи, пожалуйста, что такое магическое мышление, что такое магия, почему современная наука чем-то напоминает магию? Фраза «Любая достаточно развитая технология неотличима от магии». . .
За какое время ИИ может подобрать пароль по хешу (рассказывает Джемени ИИ)
zorxor 30.10.2026
Расскажи, Джемени, если известен хэш МД5, например, за сколько ИИ сможет подобрать соответствующий пароль? Искусственный интеллект (ИИ) может подобрать простой или средний по сложности пароль по. . .
Кто убил Suno? Почему наше солнышко погасло? (диалог с ИИ Джемени)
zorxor 30.10.2026
Как убивали Suno. Хронология. «Убийство» музыкальной нейросети Suno — это затяжной процесс, в котором технологический хайп стартапа разбился о жесткую реальность судебных исков от гигантов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru