更改类型而不更改位 [英] Changing type without changing bits

查看:184
本文介绍了更改类型而不更改位的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想取一个栈变量和 reinterpret cast 它成一个无符号的整数类型相同大小的字节。例如,我可能想要 double 值并将其转换为 uint64_t ,捕获位未修改。



如果我正在处理指针,我会使用 reinterpret_cast< uint64_t *>我已经想出了一个解决方案,它使用了一个肮脏的黑客在 reinterpret_cast

< $ c>,并且是有效的,但它需要相当多的元程序来获得一个相当简单的结果。



问题:有没有更好的方法来做到这一点?



我确实考虑过使用类型的模板联合, T 和适当大小的 int_t ,但似乎甚至哈克,似乎与未定义的行为。



编辑我明白,标准没有指定double应该是64位,如在评论中指出。但是通过一个通用的方法,我将能够得到一个无符号的整数类型,大小和double一样大,但是很大。

  #include< iostream> 

template< typename T,std :: size_t S>
struct helper {};

template< typename T>
struct helper< T,1> {
using type = uint8_t;
};
template< typename T>
struct helper< T,2> {
using type = uint16_t;
};
template< typename T>
struct helper< T,4> {
using type = uint32_t;
};
template< typename T>
struct helper< T,8> {
using type = uint64_t;
};

template< typename T>
使用int_type = typename helper< T,sizeof(T)> :: type;

template< typename T>
int_type< T> caster(T value){
int_type< T> v;
* reinterpret_cast< T *>(& v)= value;
return v;
}

int main(void){
{
auto val = caster(0。);
static_assert(std :: is_same< uint64_t,decltype(val)> :: value,no good);
std :: cout<< sizeof(val)* 8 < < val<< std :: endl;
}

{
auto val = caster(0.f);
static_assert(std :: is_same< uint32_t,decltype(val)> :: value,no good);
std :: cout<< sizeof(val)* 8 < < val<< std :: endl;
}

{
auto val = caster(-0。);
static_assert(std :: is_same< uint64_t,decltype(val)> :: value,no good);
std :: cout<< sizeof(val)* 8 < < val<< std :: endl;
}

{
auto val = caster(-0.f);
static_assert(std :: is_same< uint32_t,decltype(val)> :: value,no good);
std :: cout<< sizeof(val)* 8 < < val<< std :: endl;
}

return 0;
}

使用gcc编译上面的代码:

 > g ++ --version 
g ++(GCC)4.8.2 20131016(Cray Inc.)

> g ++ -std = c ++ 11 test.cpp&& ./a.out
64 0
32 0
64 9223372036854775808
32 2147483648


解决方案

如果你不想因为违反别名限制而有未定义的行为(C ++ 11 3.10 / 10),那么你需要访问对象表示为字符:

 模板< typename T> 
int_type< T> caster(const T& value){
int_type< T> v;
static_assert(sizeof(value)== sizeof(v),);
std :: copy_n(reinterpret_cast< const char *>(& value),
sizeof(T),
reinterpret_cast< char *>(& v));
return v;
}

高质量的编译器将优化拷贝。例如,此程式:

  int main(){
return caster(3.14f);
}

有效地优化到返回1078523331; 在英特尔处理器上


I want to take a stack variable and reinterpret cast it into an unsigned integer type of the same size in bytes. For example, I might want to take double value and cast it to an uint64_t, with the catch that the bits are not modified. And I want to do it in a generic fashion.

If I was dealing with pointers, I would use a reinterpret_cast<uint64_t*>(double_ptr).

I have come up with a solution, which uses a dirty hack on reinterpret_cast, and is effective, but it requires quite a lot of meta-programming to get a fairly simple outcome.

The question: is there a better way to do this? I am sure that there is, and that I am making this more complicated than need be.

I did think about using a templated union of type T and appropriately sized int_t, but that seemed even hackier, and seemed to play with undefined behavior.

edit I understand that the standard doesn't specify that double should be 64 bits, as pointed out in the comments. But with a generic approach, I will be able to get an unsigned integral type the same size as double, however big that is.

#include <iostream>

template <typename T, std::size_t S>
struct helper {};

template <typename T>
struct helper<T, 1> {
    using type = uint8_t;
};
template <typename T>
struct helper<T, 2> {
    using type = uint16_t;
};
template <typename T>
struct helper<T, 4> {
    using type = uint32_t;
};
template <typename T>
struct helper<T, 8> {
    using type = uint64_t;
};

template <typename T>
using int_type = typename helper<T, sizeof(T)>::type;

template <typename T>
int_type<T> caster(T value) {
    int_type<T> v;
    *reinterpret_cast<T*>(&v) = value;
    return v;
}

int main(void) {
    {
    auto val = caster(0.);
    static_assert(std::is_same<uint64_t, decltype(val)>::value, "no good");
    std::cout << sizeof(val)*8 << " " << val << std::endl;
    }

    {
    auto val = caster(0.f);
    static_assert(std::is_same<uint32_t, decltype(val)>::value, "no good");
    std::cout << sizeof(val)*8 << " " << val << std::endl;
    }

    {
    auto val = caster(-0.);
    static_assert(std::is_same<uint64_t, decltype(val)>::value, "no good");
    std::cout << sizeof(val)*8 << " " << val << std::endl;
    }

    {
    auto val = caster(-0.f);
    static_assert(std::is_same<uint32_t, decltype(val)>::value, "no good");
    std::cout << sizeof(val)*8 << " " << val << std::endl;
    }

    return 0;
}

compiling the code above with gcc gives:

> g++ --version
g++ (GCC) 4.8.2 20131016 (Cray Inc.)

> g++ -std=c++11 test.cpp && ./a.out
64 0
32 0
64 9223372036854775808
32 2147483648

解决方案

If you don't want to have undefined behavior due to violating the aliasing restrictions (C++11 3.10/10) then you need to access the object representations as characters:

template <typename T>
int_type<T> caster(const T& value) {
    int_type<T> v;
    static_assert(sizeof(value) == sizeof(v), "");
    std::copy_n(reinterpret_cast<const char*>(&value),
                sizeof(T),
                reinterpret_cast<char*>(&v));
    return v;
}

High quality compilers will optimize the copy away. E.g., this program:

int main() {
    return caster(3.14f);
}

effectively optimizes to return 1078523331; on Intel processors.

这篇关于更改类型而不更改位的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆