最近,在将我们的应用程序从gcc-5.3移植到8.2时,我们注意到一个奇怪的行为破坏了我们的应用程序。
简而言之,似乎gcc-8.2删除了我们的“ if分支,它比较2个无符号整数”之一,甚至没有发出警告。
我们使用相同的编译选项尝试了g ++ 5.3,g ++ 7.4和g ++ 8.2,只有g ++ 8.2出现了此问题。下面将显示一个简短示例。
#include <iostream>
#include <cstdint>
#include <cstdlib>
#include <cstring>
using namespace std;
struct myunion {
myunion(uint32_t x) {
_data.u32 = x;
}
uint16_t hi() const { return _data.u16[1]; }
uint16_t lo() const { return _data.u16[0]; }
union {
uint16_t u16[2];
uint32_t u32;
} _data;
};
__attribute__((noinline)) void printx1x2(uint32_t x1, uint32_t x2) {
cout << "x1: " << x1 << endl;
cout << "x2: " << x2 << endl;
} …Run Code Online (Sandbox Code Playgroud) 计算浮点数的基数2对数的整数部分的有效方法是什么?就像是
N = ceil( log2( f ))
Run Code Online (Sandbox Code Playgroud)
要么
N = floor( log2( f ))
Run Code Online (Sandbox Code Playgroud)
浮点数f.我想这可能会以某种方式非常有效地实现,因为可能只需要访问浮点指数.
编辑2:我对准确性并不感兴趣.我可以容忍+ -1的错误.我列举了这两个变种只是作为一个例子,因为一个可能在计算上比另一个更便宜(但我不知道).
我需要这个用于算法的精确控制,其中参数f是一些容差,并且需要日志来控制术语的数量.准确计算日志并不重要.
编辑:这不是要求整数参数的log2的其他许多问题的重复(例如,如何在C++中执行整数log2()?).这是关于浮点论证和完全不同的故事.具体来说,我需要f <1,这对于整数方法是不可能的
打字
指针别名的一种形式,其中两个指针指向内存中的相同位置,但将该位置表示为不同类型.编译器会将"双关语"视为不相关的指针.类型惩罚有可能导致通过两个指针访问的任何数据的依赖性问题.
文章试图说什么?
我从网络接收缓冲区,该缓冲区被转换为32位字的数组.我有一个单词被我的界面文档定义为ieee float.我需要从缓冲区中提取这个单词.在不调用转换的情况下从一种类型转换为另一种类型很困难.这些位已经符合ieee浮点标准,我不想重新排列任何位.
我的第一次尝试是将地址转换uint32_t为a void*,然后将其转换void*为a float*,然后取消引用为float:
float ieee_float(uint32_t f)
{
return *((float*)((void*)(&f)));
}
Run Code Online (Sandbox Code Playgroud)
错误:解除引用类型惩罚指针将破坏严格别名规则[-Werror = strict-aliasing]
我的第二次尝试是这样的:
float ieee_float(uint32_t f)
{
union int_float{
uint32_t i;
float f;
} tofloat;
tofloat.i = f;
return tofloat.f;
}
Run Code Online (Sandbox Code Playgroud)
然而,街上的一句话是,工会完全不安全.从最近没有编写的联合成员读取它是未定义的行为.
所以我尝试了更多的C++方法:
float ieee_float(uint32_t f)
{
return *reinterpret_cast<float*>(&f);
}
Run Code Online (Sandbox Code Playgroud)
错误:解除引用类型惩罚指针将破坏严格别名规则[-Werror = strict-aliasing]
我的下一个想法是"搞砸它.为什么我要处理指针呢?" 并尝试过:
float ieee_float(uint32_t f)
{
return reinterpret_cast<float>(f);
}
Run Code Online (Sandbox Code Playgroud)
错误:从'uint32_t {aka unsigned int}'类型无效转换为'float'类型
有没有办法在不触发警告/错误的情况下进行转换?我用g ++编译-Wall -Werror.我宁愿不接触编译器设置.
我标记了C,因为c解决方案是可以接受的.
我已经阅读了许多有关此警告的问题(取消引用类型双关指针会破坏严格别名规则,取消引用类型双关指针会破坏严格别名规则 [-Wstrict-aliasing],严格别名规则是什么?,“取消引用类型双关指针将破坏严格的别名规则”警告和其他人)并且对我的警告完全感到困惑。
所以我有一个结构:
typedef struct {
unsigned char precision;
unsigned char scale;
unsigned char array[33];
} DBNUMERIC;
Run Code Online (Sandbox Code Playgroud)
当从 MS SQL Server 检索数据时,该结构由 FreeTDS 库填充。我知道从array[1]那里开始是 64 位整数(大端),我想得到它。我使用以下代码:
int64_t result = 0;
result = be64toh(*((decltype(result)*)(numeric.array + 1)));
Run Code Online (Sandbox Code Playgroud)
但是 GCC 给了我警告dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]。但是如果我使用代码:
int64_t result = 0;
decltype(result)* temp_ptr = (decltype(result)*)(numeric.array + 1);
decltype(result) temp = *temp_ptr;
result = be64toh(temp);
Run Code Online (Sandbox Code Playgroud)
没有关于违反严格别名规则的警告。我不认为此代码与原始代码不同,因此我很困惑。如何将数组中的 8 个字节转换为int64_t变量?
在整数和整数数组之间进行类型校正是否合法?
具体代码:
#include <nmmintrin.h>
#include <stdint.h>
union Uint128 {
__uint128_t uu128;
uint64_t uu64[2];
};
static inline uint_fast8_t popcnt_u128 (__uint128_t n)
{
const union Uint128 n_u = {.uu128 = n};
const uint_fast8_t cnt_a = _mm_popcnt_u64(n_u.uu64[0]);
const uint_fast8_t cnt_b = _mm_popcnt_u64(n_u.uu64[1]);
const uint_fast8_t cnt = cnt_a + cnt_b;
return cnt;
}
Run Code Online (Sandbox Code Playgroud) 我想使用 RGBA 值表示 32 位数字,使用联合生成该数字的值是否可移植?考虑这个 C 代码;
union pixel {
uint32_t value;
uint8_t RGBA[4];
};
Run Code Online (Sandbox Code Playgroud)
这编译得很好,并且我喜欢使用它而不是一堆函数。但这安全吗?
注意:学习严格的别名规则。请耐心等待。
代码示例(t935.c):
#include <stdio.h>
int f(int* pi, double* pd)
{
*pi = 13;
*pd = 7E-323;
return *pi;
}
int main(void)
{
union { int i; double d; } u;
printf("%d\n", f(&u.i, &u.d));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
调用:
$ gcc t935.c -Wall -Wextra -std=c11 -pedantic && ./a.exe
14
$ gcc t935.c -Wall -Wextra -std=c11 -pedantic -O2 && ./a.exe
13
$ gcc t935.c -Wall -Wextra -std=c11 -pedantic -O2 -fno-strict-aliasing && ./a.exe
14
Run Code Online (Sandbox Code Playgroud)
问题:将指向同一联合成员的两个指针传递给函数是否违反了严格的别名规则?
这个问题源于Unions 和 type-punning。
UPD20210901
如果在全局范围内定义联合类型会发生什么?
对于“u …
我正在将FORTRAN 77代码的一部分转换为C++
DIMENSION ARRAY(513),JRRAY(2,513)
EQUIVALENCE (ARRAY(1),JRRAY(1,1))
Run Code Online (Sandbox Code Playgroud)
这是隐式代码,其中以I,J,K,L,M,N,O,P开头的每个变量名称被隐式地视为整数类型.因此,这里我们有一个名为ARRAY的双精度数组和一个名为JRRAY的整数数组.
等价语句将两个数组的开头指向相同的内存位置.然而,不知何故,当调用ARRAY(I)时,字节被不同地解释为double;当调用JRRAY(I,J)时,字节被解释为整数(至少这就是我认为会发生什么).
在C++中是否有类似的方式可以将相同的内存位置解释为不同的类型?
或者与FORTRAN中的EQUIVALENCE相同的东西,但是在C++中.
我已经将C++代码转换为具有高优化级别的程序集
#include <iostream>
using namespace std;
int main()
{
float sum=0;
for(int i = 0; i < 10; i++)
sum += 1.0f/float(i+1);
cout<<sum<<endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
通过
g++ -O3 -S main.cpp
g++ -O3 main.cpp && ./a.out
Run Code Online (Sandbox Code Playgroud)
结果是
2.92897
但是当我将它转换为汇编时,我没有意识到这个数字的位置.应该有一个循环或(如果展开)最终结果2.92897.但我在以下代码中找不到它:
.file "main.cpp"
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB1561:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $_ZSt4cout, %edi
movsd .LC0(%rip), %xmm0
call _ZNSo9_M_insertIdEERSoT_
movq %rax, %rdi
call _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
xorl %eax, %eax
addq $8, %rsp
.cfi_def_cfa_offset …Run Code Online (Sandbox Code Playgroud) c++ ×6
c ×4
g++ ×2
gcc ×2
type-punning ×2
unions ×2
arrays ×1
assembly ×1
c11 ×1
casting ×1
constraints ×1
equivalence ×1
fortran ×1
gcc-warning ×1
ieee-754 ×1
pointers ×1
x86 ×1
x86-64 ×1