Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Type casting без изменения содержимого

17K
07 августа 2007 года
vagran
25 / / 11.11.2006
В общем, надо переменной целого типа присваивать значения числа с плавающей точкой, причём не конвертировать из одного типа в другой, а копировать 4 байта внутреннего представления числа. Я понимаю, что можно использовать union, но мне просто интересно, есть ли способ в C++ сделать так. Если речь идёт о двух переменных, то копирование 4 байт можно сделать как угодно, например, так:
 
Код:
float f=1;
int i;
i=*(int *)&f;

Несмотря на длинное выражение, мой борландовский компилятор компилирует это в одну команду mov, как и надо.
Но как быть, если я хочу присвоить константное значение:
 
Код:
int i=(float)1;

После выполнения этого кода в переменной i всё-равно будет значение 1 (целое), а я бы хотел 0x3f800000 - внутреннее представление единицы в формате float. Неужели в таком мощном языке нет средств для такой простой вещи?

И попутно по поводу инициализации union:
 
Код:
union
{
int i;
float f;
}u={1};

Этот код присвоит единицу типу int, иначе говоря первому объявленному члену. А можно ли таким образом инициализировать тип float, т.е. не первый объявленный член?
1.9K
07 августа 2007 года
[*]Frosty
278 / / 17.06.2006
Классный вопрос! Даже два.
Насчет первого походу нет.
А насчет второго
Цитата:
И попутно по поводу инициализации union:
 
Код:
union
{
int i;
float f;
}u={1};

Этот код присвоит единицу типу int, иначе говоря первому объявленному члену. А можно ли таким образом инициализировать тип float, т.е. не первый объявленный член?


еще чище)
Походу С99 должен подерживать именованые инициализаторы, помоему это так называеться, тоесть должен работать следующий код

 
Код:
union
{
int i;
float f;
}u={.f = 1};

Но как насчет C++ непомню) Builder ругаеться Другое не пробывал) Завтра уж) Спать пора)))
361
07 августа 2007 года
Odissey_
661 / / 19.09.2006
как с помощью с++ сделать незнаю.
в си можно рискнуть так -
 
Код:
int i2;
sscanf("1","%f",&i2);

но асм кода будет больше чем в твоем примере
а асм вставки в код неподходят?

//+add
да уж, ночь дает о себе знать...
какие асм вставки, какие sscanf....

хочешь константу так используй константу
 
Код:
i = 0x3f800000L;
309
07 августа 2007 года
el scorpio
1.1K / / 19.09.2006
Цитата:
int i=(float)1;


Всё правильно - ведь манипуляции с адресами нет. По-этому сначала 1 приводится к типу float, а потом - к типу int.

353
07 августа 2007 года
Nixus
840 / / 04.01.2007
Почему это нельзя? А так?
 
Код:
int i = *((int*)(&((const float&)1)));
17K
07 августа 2007 года
vagran
25 / / 11.11.2006
Цитата:
Походу С99 должен подерживать именованые инициализаторы, помоему это так называеться, тоесть должен работать следующий код
Код:
union
{
int i;
float f;
}u={.f = 1};


Есть такое, только в C++ это не работает. Опять напрашивается вопрос, неужели такую возможность выкинули и не придумали никакой замены? Я сколько не искал, ничего не нашёл :-(

Цитата:
как с помощью с++ сделать незнаю.
в си можно рискнуть так -
Код:
int i2;
sscanf("1","%f",&i2);
но асм кода будет больше чем в твоем примере


Не имеет смысла вызывать такую сложную функцию (в плане производительности), чтобы просто скопировать 4 байта, которые компилятор и так представляет в нужном виде.

Цитата:
а асм вставки в код неподходят?

хочешь константу так используй константу
Код:
i = 0x3f800000L;


Асм вставки не подходят, т.к. хочется использовать именно возможности синтаксиса C++. А насчёт константы, так ведь в том и дело, чтобы не "вручную" высчитывать её, а предоставить это компилятору. Кстати, для асма тут вообще нет никаких проблем с типами:

 
Код:
i dd 1.0

и используй дальше dword ptr где хочешь. Кстати, вот такой код
 
Код:
register float f;
f=1;

после компиляции будет выглядить примерно так:
 
Код:
mov edx,3f800000h

Т.е. компилятор в целях оптимизации всё равно внутри представляет вещественные числа как целые. Логично, что всё таки должен был бы быть какой-то способ для решения поставленной выше задачи.

Цитата:
Почему это нельзя? А так?
Код:
int i = *((int*)(&((const float&)1)));


Честно говоря, не совсем понял. ((const float&)1) (или скорей всего так: ((const float&)1.0) ) - уже никак (Can not cast from int/double to const float &). Вообще, амперсанд перед константой никак не поставить (Must take address of a memory location). Может есть другие способы?

Мою проблему решил бы ответ на любой из двух вопросов, может кто-то всё-таки знает, как быть с union'ом?

353
07 августа 2007 года
Nixus
840 / / 04.01.2007
Цитата: vagran

 
Код:
register float f;
f=1;

после компиляции будет выглядить примерно так:
 
Код:
mov edx,3f800000h

Т.е. компилятор в целях оптимизации всё равно внутри представляет вещественные числа как целые. Логично, что всё таки должен был бы быть какой-то способ для решения поставленной выше задачи.


Кстати говоря, неверно. Числа с плавающей запятой на процессорах x86 не хранятся в регистрах общего назначения. Поэтому не храняться как чаcть команд типа mov. Сами константы лежат в памяти и загружаются оттуда в стек FPU.

Цитата: vagran

Честно говоря, не совсем понял. ((const float&)1) (или скорей всего так: ((const float&)1.0) ) - уже никак (Can not cast from int/double to const float &). Вообще, амперсанд перед константой никак не поставить (Must take address of a memory location). Может есть другие способы?

Мою проблему решил бы ответ на любой из двух вопросов, может кто-то всё-таки знает, как быть с union'ом?


Чего тут понимать? Сначала получаем ссылку на константу типа float, потом получаем ее адрес, затем переводим в указатель типа int, разименовав его получаем то, что нужно.
Код получается примерно следующий:

 
Код:
mov DWORD PTR $T547[ebp], 1065353216    ; 3f800000H
    mov eax, DWORD PTR $T547[ebp]
    mov DWORD PTR ?i@@3HA, eax


Ну напиши так:
 
Код:
int i = *((int*)(&((const float&)(float)1.0)));

и тогда не важно как ты пишешь... 1, 1.0, 1.f или еще как.
Если пугает огромная конструкция, то пишем define:
 
Код:
#define float2int(v) (*((int*)(&((const float&)(float)(v)))))
int i = float2int(1.0);
17K
07 августа 2007 года
vagran
25 / / 11.11.2006
Цитата:
Кстати говоря, неверно. Числа с плавающей запятой на процессорах x86 не хранятся в регистрах общего назначения. Поэтому не храняться как чаcть команд типа mov. Сами константы лежат в памяти и загружаются оттуда в стек FPU.


В стек FPU числа из памяти загружаются только непосредственно перед выполнением арифметической операции, а до этого они могут храниться где угодно. Согласен, пример не совсем удачный (лучше было бы, например, mov dword ptr [ebp-8h],3f800000h, но суть не меняется), но и такая ситуация возможна, например, если эта переменная не используется непосредственно в арифметических операциях в контексте функции, где она объявлена, а только передаётся другим функциям (не обязательно __fastcall, push edx ничем не хуже) в качестве аргумента. :p

Цитата:
int i = *((int*)(&((const float&)(float)1.0)));


Я же говорю, у меня этот код не работает (Can not cast from 'float' to 'const float &'), хотя у меня складывается впечатление, что у уважаемого тов. Nixus'а всё прекрасно компилируется. В чём может быть дело?

361
07 августа 2007 года
Odissey_
661 / / 19.09.2006
у меня тоже компилится без проблем...
а насчет union, так не пробывал ?
 
Код:
struct foo
{
    union
        {
            int i;
            float f;
        };
    foo(int val):f((float)val){}
};

но за результат не ручаюсь, счас попробывать негде
17K
07 августа 2007 года
vagran
25 / / 11.11.2006
Цитата:
у меня тоже компилится без проблем...


А какой у вас компилятор? C++? У меня Borland C++ 5.2

Цитата:
struct foo
{
union
{
int i;
float f;
};
foo(int val):f((float)val){}
};


Спасибо, мысль понял. Только одна неточность, в конструктор лучше передавать параметр типа float, потому что, иначе он сначала урезается до целой части, а потом присваивается члену f, и код

 
Код:
foo myfoo(3.14);

приведёт к тому, что в f будет 3.0.

Вот моя версия этого кода:
 
Код:
struct foo
{
    union
        {
            int i;
            float f;
        };
    foo(float val,bool wantfloat){if (wantfloat) f=val;else i=val;}
}myfoo(3.14,1);

Можно сказать, что вопрос с union'ом решён (хотя здесь и вызывается коструктор, так можно и правда sscanf() использовать), ну а если у всех кроме меня :( ещё и компилируется вышеприведённое безобразие, то, наверное, все ответы даны. Всем за них большое спасибо!
353
07 августа 2007 года
Nixus
840 / / 04.01.2007
Цитата: vagran
В стек FPU числа из памяти загружаются только непосредственно перед выполнением арифметической операции, а до этого они могут храниться где угодно. Согласен, пример не совсем удачный (лучше было бы, например, mov dword ptr [ebp-8h],3f800000h, но суть не меняется), но и такая ситуация возможна, например, если эта переменная не используется непосредственно в арифметических операциях в контексте функции, где она объявлена, а только передаётся другим функциям (не обязательно __fastcall, push edx ничем не хуже) в качестве аргумента. :p


Возможно все. Но приведенный вами пример был не верен. Я это показал.

Цитата: vagran
Я же говорю, у меня этот код не работает (Can not cast from 'float' to 'const float &'), хотя у меня складывается впечатление, что у уважаемого тов. Nixus'а всё прекрасно компилируется. В чём может быть дело?


В MSVC++ 6.0 все компилиться и без предупреждений даже.
Можно сделать к примеру вот так

 
Код:
int i;
    *((float*)&i) = 1;

Но это уже опаснее, т.к. на некоторых машинах sizeof(int). может быть меньше sizeof(float).
17K
07 августа 2007 года
vagran
25 / / 11.11.2006
Цитата:
В MSVC++ 6.0 все компилиться и без предупреждений даже.

Действительно, в MSVC++ работает. А чё у них стандарты разные что ли?

Цитата:

*((float*)&i) = 1;


Такой способ мне не подходит.
Пожалуй, объясню, зачем мне всё это. У меня есть структура, в которой имеется член типа unsigned int, и в котором физически могут хранится и целые, и вещественные числа, и указатели на строки и что угодно :-). Вот фрагмент моего кода:

Код:
.....
//variable flags and types
#define u_varf_readonly 0x100
#define u_varf_user 0x200

#define u_vart_boolean 0x1
#define u_vart_int 0x2
#define u_vart_float 0x3
#define u_vart_string 0x4

typedef struct uvar_t *puvar_t;
typedef bool (*uvarparser_t)(puvar_t pvar,dword value,dword operation,pointer param);//ret false to execute standart action
//variables parser operations
#define uvar_parserop_read 1//(dword *)param - pointer to buffer for value storing
#define uvar_parserop_write 2
#define uvar_parserop_str 3//(char *)param - pointer to buffer for string
#define uvar_parserop_val 4//(char *)value - string,(dword *)param - buffer for value
#define uvar_parserop_remove 5

#define uvar_maxname 32
typedef struct uvar_t
{
    dword value;//32-bit value can be any of the types listed above
    char name[uvar_maxname];
    dword flags;//high-order word can be used by user
   uvarparser_t parser;
   puvar_t prev,next;
}*puvar_t;

#define uVarFloat(x) (*(float *)&x)
#define uVarDword(x) (*(dword *)&x)
.....


... и далее в приложении, например, так:
 
Код:
uvar_t screen_showfps={1,"screen_showfps",u_vart_boolean};
....
    uVarRegister(&screen_showfps);

Всего две простые строки и в консоли появляется новая переменная, причём её текущее значение всегда доступно по screen_showfps.value, которое без труда можно привести к любому типу. Как видите, мне важна максимальная простота инициализации. Когда дело дошло до значений типа float, оказалось, что не всё так просто и пришлось делать так:
 
Код:
float md_f1=1;
uvar_t md_timecomp={uVarDword(md_f1),"md_timecompression",u_vart_float};

создавать для этих целей отдельную переменную не очень удобно и громоздко. Предложенный Nixus'ом вариант подошёл бы, если бы работал в Борланде, но увы...:(
17K
07 августа 2007 года
vagran
25 / / 11.11.2006
Урааа!!! Решение найдено мною самостоятельно!!! Ответ: перегружаемые конструкторы структур. Зацените:
 
Код:
struct foo
{
   union
   {
      int i;
      float f;
   };
   foo (int val) {i=val;}
   foo (float val) {f=val;}
}myfoo_int(1),myfoo_float(1.0f);

Адаптировать это под мою задачу - дело техники. Всем огромное спасибо!!
350
08 августа 2007 года
cheburator
589 / / 01.06.2006
Такой вот код изобрел. Может показаться немного глупым :)
Код:
struct float_and_int
{
  union
  {
    int i;
    float f;
  } data;
  float_and_int(int i)
  {
    data.i = i;
  };
  float_and_int(float f)
  {
    data.f = f;
  }
  template<typename T>
    const T get ()
  {
    terminate(); // Если вдруг параметр шаблона T - ни int, ни float
  }
  template<>
    const int get<int> ()
  {
    return data.i;
  }
  template<>
    const float get<float> ()
  {
    return data.f;
  }
 
}

void main (void)
{
  float_and_int fi (1.0);
  cout << fi.get<int>();
};
3
08 августа 2007 года
Green
4.8K / / 20.01.2000
Я как-то приводил мсходники своего HookApi (http://sources.codenet.ru/download/1225/HookApi_v2_0.html).
Там я написал функцию super_cast:
 
Код:
template<typename T, typename U>
T super_cast(const U& u) {
    return *(T*)&u;
}

Используется так же как и любые др. cast-ы:
 
Код:
int i = super_cast<int>(1.f);
17K
08 августа 2007 года
vagran
25 / / 11.11.2006
Митинг стихийно продолжается, вставлю и я своё веское слово. Кажется, большинство всё-таки не поняло, что я хотел сказать, и я всё-таки решил сам во всём разобраться. (Извините, что я везде привожу ассемблерный код, но я не знаю, как по другому выразить свои мысли :-)

Для начала я решил понять, почему код Nixus'a
Цитата:
int i = *((int*)(&((const float&)(float)1.0)));


нормально компилируется под MSVC++ и не компилится совсем под Борландом. А для этого посмотрел как выглядит код

 
Код:
float f;
f=2;//внутреннее представление двойки 0x40000000

после этих компиляторов. MSVC++ 8.0 выдал это:
 
Код:
fld         dword ptr [__real@40000000 (41673Ch)]
fstp        dword ptr [f]

а Борланд 5.2 ответил вот этим:
 
Код:
mov [ebp-0x08],0x40000000

В принципе, для людей знающих здесь всё ясно, Microsoft однозначно в глубокой...отдыхает он, в общем. Для остальных поясню: Борланду для выполнения этой задачи понадобилась одна простая команда CPU (и это правильно), MSVC же делает два обращения к FPU (а каждое такое обращение это уже операция повышенной сложности, которая отнимает тактов намного больше, чем просто команда CPU) и к тому же, для константы '2' (которая и используется-то всего один раз) отводит 4 байта в сегменте данных. А ведь надо всего-то по указанному адресу положить нужные 4 байта, FPU не для этого был придуман. Именно поэтому под MSVC возможно делать адресацию константы, ведь она постоянно хранится в памяти, делать ту манипуляцию с адресами, а в Борланде такой номер не пройдёт, даже переменной типа double константа будет присваиваться за две команды CPU, младшая часть, потом старшая, и я с ними полностью согласен. Под Борланд ассемблерный код этого куска:
 
Код:
int i;
i=0x40000000;

будет выглядеть точно также, как и предыдущий пример. Именно поэтому у меня и напросился мой первый вопрос, который я сформулировал, наверно не совсем правильно, надо было так: "хочу константу типа int, которая является внутренним представлением константы x типа float" :-) Причём, непременно, в Борланде. Код
Цитата:

*((float*)&i) = 1;


конечно же даст нужный результат, но константа как была типа float, так с ним и осталась, манипуляции проходят с типом переменной и инициализировать структуру таким кодом я не смогу (в смысле методом ..={...}, конечно).

А как вам такой код:

 
Код:
//Решаем в MSVC обратную задачу
float f;
f=*((float*)(&((const int&)2)));

...получаем:
0041137E  mov         dword ptr [ebp-0C8h],2
00411388  fld         dword ptr [ebp-0C8h]
0041138E  fstp        dword ptr [f (417178h)] ;теперь у нас в dword ptr [f] находится 0x00000002, зашибись

Увидев это чудо, программисты-системщики пойдут с транспорантами сжигать чучело Билла Гейтса у стен американского посльства.

Цитата:
Я как-то приводил мсходники своего HookApi (http://sources.codenet.ru/download/1...Api_v2_0.html).
Там я написал функцию super_cast:
Код:
template<typename T, typename U>
T super_cast(const U& u) {
return *(T*)&u;
}
Используется так же как и любые др. cast-ы:
Код:
int i = super_cast<int>(1.f)


Я не подразумевал использование вызовов каких-либо функций. Взгляните на код:

 
Код:
mov [ebp-0x04],0x3f800000 ;а ведь уже здесь вместо [ebp-0x04] могло быть
lea edx,[ebp-0x04]
push edx
call super_cast; а там ещё куча всякого безобразия
pop ecx
mov ,eax; и в eax, естественно, всё тот же 0x3f800000, круто :-)

Ну и если до конца добить этот пример, то можно сказать, что внутри функции super_cast меняется тип переменной, а не константы (аргумент функции - всё те же 4 байта в стековом кадре, ничем не хуже любой другой локальной переменной, на которую можно получить указатель).

Ну и если подытожить, то, судя по всему, на мой первый вопрос в природе нет ответа. Зато, теперь наверное, на всю жизнь запомню, как выглядит единица изнутри :)
17K
08 августа 2007 года
vagran
25 / / 11.11.2006
Даже сам обосную почему ответа нет, чтобы это не сделал за меня кто-нибудь другой :). По сути я хочу, чтобы тип float чудесным образом превратился int напрямую, без лишних манипуляций с адресами и промежуточными сохранениями данных в памяти. Но ведь язык C разрабатывался как кроссплатформенный и на разных машинах эти типы могут иметь разный размер и хранится в разных местах, поэтому такое преобразования противоречит концепции языка, правильного ответа не может быть в принципе. Извиняюсь, что задал такой вопрос.
353
08 августа 2007 года
Nixus
840 / / 04.01.2007
Цитата: vagran
....посмотрел как выглядит код
 
Код:
float f;
f=2;//внутреннее представление двойки 0x40000000

после этих компиляторов. MSVC++ 8.0 выдал это:
 
Код:
fld         dword ptr [__real@40000000 (41673Ch)]
fstp        dword ptr [f]

а Борланд 5.2 ответил вот этим:
 
Код:
mov [ebp-0x08],0x40000000



А у меня MSVC++ выдал

 
Код:
; 11   :
; 12   :    float f;
; 13   :    f=2;

    mov DWORD PTR _f$[ebp], 1073741824      ; 40000000H

Причем и в Debug и в Release. Однако странно.
350
11 августа 2007 года
cheburator
589 / / 01.06.2006
to Nixus & vagran:
Во-первых, различия в версии - последние мсвц оптимизируют гораздо лучше, при всем этом гораздо лучше отвечают стандарту. (ИМХО).
Во-вторых, и особенно это касается vagran, если уж речь идет о константах, так и надо писать:
 
Код:
const float f = 2;

Уверен, здесь не только будет использоваться CPU вместо FPU, но данные вообще будут храниться в регисте вместо памяти.
Хотя, кто знает, нужно проверить...
Уточняю (так, между прочим, информация к размышлению): согласно стандарта С++, существуют cv-квалификаторы, const означает неизменность данных вообще, volatile (применяется по умолчанию) - модифицируемость данных вообще, а есть еще const volatile - неизменность данных со стороны данного потока, учитывая то, что данные могут измениться параллельно исполняющимися потоками/процессами.
Не указав квалификатора (а значит - volatile), вы вынуждаете компилятор не применять практически никакой оптимизации, ибо данные могут измениться другим потоком, и поэтому при каждом обращении к ним со стороны текущего потока нужно считывать их из памяти.
5.3K
17 августа 2007 года
Somebody
185 / / 24.12.2006
Цитата:

int i = *((int*)(&((const float&)(float)1.0)));


У меня это в ДОСовском Borland C++ не компилируется. Но, мо-моему, есть способ проще, который, кстати, и в Pascal'е можно использовать:
*(float*)&i=1.0;

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог