理想情况下,以下代码将采用IEEE 754表示形式的浮点数并将其转换为十六进制
void convert() //gets the float input from user and turns it into hexadecimal
{
float f;
printf("Enter float: ");
scanf("%f", &f);
printf("hex is %x", f);
}
Run Code Online (Sandbox Code Playgroud)
我不太确定会出现什么问题.它将数字转换为十六进制数,但却是一个非常错误的数字.
123.1443 gives 40000000
43.3 gives 60000000
8 gives 0
Run Code Online (Sandbox Code Playgroud)
所以它正在做某事,我只是不太确定是什么.
帮助将不胜感激
我有性能关键代码,并且有一个巨大的函数,在函数开始时在堆栈上分配40个不同大小的数组.这些阵列中的大多数必须具有一定的对齐性(因为这些阵列是使用需要内存对齐的cpu指令(对于Intel和arm CPU)在链中的其他位置访问的.
由于某些版本的gcc无法正确对齐堆栈变量(特别是对于arm代码),或者甚至有时它表示目标体系结构的最大对齐小于我的代码实际请求的对齐,我别无选择,只能分配这些数组在堆栈上并手动对齐它们.
所以,对于每个数组,我需要做类似的事情才能使它正确对齐:
short history_[HIST_SIZE + 32];
short * history = (short*)((((uintptr_t)history_) + 31) & (~31));
Run Code Online (Sandbox Code Playgroud)
这样,history现在在32字节边界上对齐.对所有40个数组执行相同的操作非常繁琐,而且这部分代码实际上是cpu密集型的,我根本无法对每个数组执行相同的对齐技术(这种对齐混乱会使优化器和不同的寄存器分配混淆,从而减慢函数的使用时间,为了更好的解释,请参阅问题末尾的解释).
所以......显然,我只想做一次手动对齐,并假设这些数组一个接着一个.我还为这些数组添加了额外的填充,以便它们总是32个字节的倍数.那么,我只需在堆栈上创建一个jumbo char数组并将其转换为具有所有这些对齐数组的结构:
struct tmp
{
short history[HIST_SIZE];
short history2[2*HIST_SIZE];
...
int energy[320];
...
};
char buf[sizeof(tmp) + 32];
tmp * X = (tmp*)((((uintptr_t)buf) + 31) & (~31));
Run Code Online (Sandbox Code Playgroud)
这样的事情.也许不是最优雅的,但它产生了非常好的结果,并且对生成的组件的手动检查证明生成的代码或多或少是足够的和可接受的.构建系统已更新为使用更新的GCC,突然我们开始在生成的数据中有一些工件(例如,即使在具有禁用的asm代码的纯C构建中,验证测试套件的输出也不再精确).调试问题花了很长时间,它似乎与别名规则和更新版本的GCC有关.
那么,我该怎么做呢?请不要浪费时间试图解释它不是标准的,不是可移植的,未定义的等等(我已经阅读过很多关于此的文章).此外,我无法改变代码(我可能会考虑修改GCC以解决问题,但不能重构代码)...基本上,我想要的是应用一些黑魔法咒语以便更新的GCC为这种类型的代码生成功能相同的代码而不禁用优化?
编辑:
简而言之,问题的关键点......我如何分配随机数量的堆栈空间(使用char数组或者alloca,然后将指针对齐到该堆栈空间并重新解释这个内存块,因为某些结构具有一些定义良好的布局,只要结构本身正确对齐,我就会保证某些变量的对齐.我正在尝试使用各种方法来转换内存,我将大堆栈分配移动到一个单独的函数,仍然会导致输出错误和堆栈损坏,我我真的开始越来越多地认为这个巨大的功能会在gcc中遇到某种错误.这很奇怪,通过这样做,无论我尝试什么,我都无法完成这件事.顺便说一下,我禁用了所有需要任何对齐的优化,现在都是纯C风格的代码,我仍然得到不好的结果(非bitexact输出和偶尔的堆栈损坏崩溃).修复它的简单修复,我写而不是:
char buf[sizeof(tmp) + 32];
tmp * X = (tmp*)((((uintptr_t)buf) + 31) & (~31));
Run Code Online (Sandbox Code Playgroud)
这段代码:
tmp …Run Code Online (Sandbox Code Playgroud)(注意:虽然这个问题是关于"存储"的,但"加载"情况具有相同的问题并且是完全对称的.)
SSE内在函数提供_mm_storeu_pd具有以下签名的函数:
void _mm_storeu_pd (double *p, __m128d a);
Run Code Online (Sandbox Code Playgroud)
所以,如果我有两个双精度矢量,并且我想将它存储到两个双精度数组中,我可以使用这个内在函数.
但是,我的矢量不是两个双打; 它是两个64位整数,我想将它存储到两个64位整数的数组中.也就是说,我想要一个具有以下签名的函数:
void _mm_storeu_epi64 (int64_t *p, __m128i a);
Run Code Online (Sandbox Code Playgroud)
但内在函数没有提供这样的功能.他们最接近的是_mm_storeu_si128:
void _mm_storeu_si128 (__m128i *p, __m128i a);
Run Code Online (Sandbox Code Playgroud)
问题是这个函数需要一个指针__m128i,而我的数组是一个数组int64_t.通过错误类型的指针写入对象违反了严格的别名,并且肯定是未定义的行为.我担心我的编译器现在或将来会重新排序或以其他方式优化存储,从而以奇怪的方式破坏我的程序.
要清楚,我想要的是一个我可以这样调用的函数:
__m128i v = _mm_set_epi64x(2,1);
int64_t ra[2];
_mm_storeu_epi64(&ra[0], v); // does not exist, so I want to implement it
Run Code Online (Sandbox Code Playgroud)
以下是创建此类功能的六次尝试.
void _mm_storeu_epi64(int64_t *p, __m128i a) {
_mm_storeu_si128(reinterpret_cast<__m128i *>(p), a);
}
Run Code Online (Sandbox Code Playgroud)
这似乎有我担心的严格别名问题.
void _mm_storeu_epi64(int64_t *p, __m128i a) {
_mm_storeu_si128(static_cast<__m128i *>(static_cast<void *>(p)), a);
}
Run Code Online (Sandbox Code Playgroud)
在GoingNative活动中,在第2天的交互式面板中,在9分钟时,Chandler Carruth说:
指针会产生锯齿问题.他们放慢你的二进制文件速度而不加速它们.
这是什么意思?这可以用(简单)示例来说明吗?
我更换使用std::map与热路径CPP-B树的btree_map.但是在启用优化的情况下,GCC和Clang抱怨严格的别名违规.问题归结为:
template <typename Key, typename Value>
class btree_map {
public:
// In order to match the standard library's container interfaces
using value_type = std::pair<const Key, Value>;
private:
using mutable_value_type = std::pair<Key, Value>;
struct node_type {
mutable_value_type values[N];
// ...
};
public:
class iterator {
// ...
value_type& operator*() {
// Here we cast from const std::pair<Key, Value>&
// to const std::pair<const Key, Value>&
return reinterpret_cast<value_type&>(node->values[i]);
}
};
std::pair<iterator, bool> insert(const value_type& value) {
// …Run Code Online (Sandbox Code Playgroud) 比方说,我有一组无符号字符代表一堆POD对象(例如从套接字或通过mmap读取).它们代表哪些类型以及在运行时确定的位置,但我们假设每个类型已经正确对齐.
将这些字节"转换"为相应的POD类型的最佳方法是什么?
解决方案应该符合c ++标准(比方说> = c ++ 11)或者至少可以保证使用g ++> = 4.9,clang ++> = 3.5和MSVC> = 2015U3.编辑:在Linux,Windows上运行x86/x64或32/64位臂.
理想情况下,我想做这样的事情:
uint8_t buffer[100]; //filled e.g. from network
switch(buffer[0]) {
case 0: process(*reinterpret_cast<Pod1*>(&buffer[4]); break;
case 1: process(*reinterpret_cast<Pod2*>(&buffer[8+buffer[1]*4]); break;
//...
}
Run Code Online (Sandbox Code Playgroud)
要么
switch(buffer[0]) {
case 0: {
auto* ptr = new(&buffer[4]) Pod1;
process(*ptr);
}break;
case 1: {
auto* ptr = new(&buffer[8+buffer[1]*4]) Pod2;
process(*ptr);
}break;
//...
}
Run Code Online (Sandbox Code Playgroud)
两者似乎都有效,但两者都是c ++中的AFAIK未定义行为1).而且只是为了完整性:我知道将通常的东西复制到适当的局部变量中的"通常"解决方案:
Pod1 tmp;
std::copy_n(&buffer[4],sizeof(tmp), reinterpret_cast<uint8_t*>(&tmp));
process(tmp);
Run Code Online (Sandbox Code Playgroud)
在某些情况下,它可能不是其他人的开销,在某些情况下甚至可能更快,但性能除外,我不再能够修改数据并且说实话:它让我很生气,知道我有右位在内存中的适当位置,但我不能使用它们.
我想出的一个有点疯狂的解决方案是:
template<class T>
T* inplace_cast(uint8_t* data) {
//checks omitted for …Run Code Online (Sandbox Code Playgroud) 根据我对严格别名规则的理解,这个快速反平方根的代码将导致C++中未定义的行为:
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // type punning
i = 0x5f3759df - ( i >> 1 );
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) );
return y;
}
Run Code Online (Sandbox Code Playgroud)
这段代码确实会导致UB吗?如果是,如何以符合标准的方式重新实现?如果没有,为什么不呢?
假设:在调用此函数之前,我们已经以某种方式检查了浮点数是IEEE 754 32位格式, …
考虑一下:
int main(int, char **) {
int variable = 21;
int array[1] = {21};
using ArrayOf1Int = int[1];
(*reinterpret_cast<ArrayOf1Int *>(&variable))[0] = 42;
*reinterpret_cast<int *>(&array) = 42;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我是否违反了严格的别名规则?
或者,正如在这条引导我这个问题的评论中一样:变量是一个大小为1的数组吗?
请注意,我将此标记为语言律师问题.因此,我对-fno-strict-aliasing编译器的特定行为不感兴趣,而是在标准中所说的内容.另外我认为知道C++ 03,C++ 11,C++ 14和更新版本之间是否以及如何变化将会很有趣.
我想编写一个函数,输入一个数据数组并使用指针输出另一个数据数组。
我想知道如果两者都指向同一个地址会产生什么结果src,dst因为我知道编译器可以针对 const 进行优化。这是未定义的行为吗?(我标记了 C 和 C++,因为我不确定它们之间的答案是否不同,我想了解两者。)
void f(const char *src, char *dst) {
dst[2] = src[0];
dst[1] = src[1];
dst[0] = src[2];
}
int main() {
char s[] = "123";
f(s,s);
printf("%s\n", s);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
除了上面的问题,如果我删除const原来的代码,这个定义是否明确?
c++ ×8
c ×4
c++11 ×2
b-tree ×1
c++14 ×1
c++17 ×1
casting ×1
constants ×1
ieee-754 ×1
intrinsics ×1
move ×1
performance ×1
sse ×1
type-punning ×1