作为我编写的程序的一部分,我需要比较形式为a + sqrt(b)where a和bunsigned integer的两个值。由于这是紧密循环的一部分,因此我希望此比较尽可能快地运行。(如果重要的话,我正在x86-64机器上运行代码,并且无符号整数不大于10 ^ 6。此外,我知道这样的事实a1<a2。)
作为独立功能,这就是我要优化的功能。我的数字是足够小的整数,可以double(或什至float)精确地表示它们,但是sqrt结果中的舍入误差不能改变结果。
// known pre-condition: a1 < a2 in case that helps
bool is_smaller(unsigned a1, unsigned b1, unsigned a2, unsigned b2) {
return a1+sqrt(b1) < a2+sqrt(b2); // computed mathematically exactly
}
Run Code Online (Sandbox Code Playgroud)
测试用例:is_smaller(900000, 1000000, 900001, 998002)应返回true,但如@wim注释所示,sqrtf()将其返回false。因此将(int)sqrt()截断为整数。
a1+sqrt(b1) = 90100和a2+sqrt(b2) = 901000.00050050037512481206。最接近的浮点数就是90100。
由于sqrt()即使在现代的x86-64上作为sqrtsd指令完全内联时,该函数通常也非常昂贵,所以我试图避免sqrt()尽可能地调用。
通过平方运算删除sqrt还可以通过使所有计算都精确来避免舍入错误的任何危险。
如果相反,功能是这样的...
bool is_smaller(unsigned …Run Code Online (Sandbox Code Playgroud) 假设我们有两个寄存器长度为 2有符号1 的整数,例如a和b。我们想要计算值(a + b) / 2,向上舍入、向下舍入、向零舍入或远离零舍入,无论哪种方式更容易(即我们不关心舍入方向)。
结果是另一个寄存器长度有符号整数(很明显,平均值必须在寄存器长度有符号整数的范围内)。
\n执行此计算最快的方法是什么?
\n您可以选择两个整数最初位于哪个寄存器中,以及平均值最终位于哪个寄存器中。
\n脚注1:对于无符号整数,我们可以用两条指令来完成。尽管循环进位在 Intel CPU 上超过 1 uop,但这可能是最快的方法。但当计数仅为 1 时,只有一对。 关于无符号均值的问答中的答案讨论了效率。
\nadd rdi, rsi\nrcr rdi, 1\nRun Code Online (Sandbox Code Playgroud)\nrdi这两个数字以和开始rsi,平均值以 结束rdi。但对于有符号数,-1 + 3将设置 CF,并将 a 旋转1到符号位。没有给出正确答案+1。
脚注 2:我指定了寄存器长度的有符号整数,这样我们就不能简单地用movsxdorcdqe指令对整数进行符号扩展。
我得到的最接近的解决方案使用四个指令,其中一个rcr在 Intel 上为 3 uops,在 AMD …
最近,我遇到了这个答案,它描述了如何初始化一个std::array非默认可构造元素.我并不感到惊讶,因为这个答案显然没有做任何默认构建.
相反,它std::array使用聚合初始化构建临时,然后移动(如果移动构造函数可用)或在函数返回时复制到命名变量.所以我们只需要移动构造函数或复制构造函数即可.
或者我认为......
然后是这段代码让我感到困惑:
struct foo {
int x;
foo(int x) : x(x) {}
foo() = delete;
foo(const foo&) = delete;
foo& operator=(const foo&) = delete;
foo(foo&&) = delete;
foo& operator=(foo&&) = delete;
};
foo make_foo(int x) {
return foo(x);
}
int main() {
foo f = make_foo(1);
foo g(make_foo(2));
}
Run Code Online (Sandbox Code Playgroud)
所有五个特殊成员构造函数/运算符都被显式删除,所以现在我不能从返回值构造我的对象,对吗?
错误.
令我惊讶的是,这在gcc中编译(使用C++ 17)!
为什么编译?显然foo要从函数返回一个make_foo(),我们必须构造一个foo.这意味着在main()我们foo从返回的函数中分配或构造一个函数foo.怎么可能?!
在此示例中,coord_squared_t是整数类型的别名,其大小至少是整数类型的两倍coord_t:
typedef int_least32_t coord_t;
coord_squared_t CalculateSquaredHypothenuse(coord_t x, coord_t y){
coord_squared_t _x=x;
coord_squared_t _y=y;
return _x*_x+_y*_y;
}
Run Code Online (Sandbox Code Playgroud)
什么可以用来表现coord_squared_t在以下方面coord_t?标准库中是否有任何东西可以让我做一些像double_width<coord_t>::type获得正确宽度的东西,而不是明确选择类型?
C++ 11或C++ 14很好.
我今天注意到boost::optional::is_initialized()在Boost 1.64.0参考中被标记为已弃用.我的项目大量洒满,is_initialized()以检查是否boost::optional包含值.
我没有看到任何其他方法来正确测试是否boost::optional初始化,我错过了什么?
该boost::optional有一个explicit operator bool(),这意味着我可以做的if(foo){...},如果foo是一个boost::optional.然而,这是否会给出错误的结果foo是一个boost::optional<bool>或一些其他boost::optional<T>地方T可转换为bool.
Boost期望用户做什么?
这是一段无法在MSVC 2015中编译的代码(忽略未初始化的值访问):
#include <array>
int main() {
constexpr int x = 5;
auto func = []() {
std::array<int, x> arr;
return arr[0];
};
func();
}
Run Code Online (Sandbox Code Playgroud)
它抱怨说:
'x' cannot be implicitly captured because no default capture mode has been specified
Run Code Online (Sandbox Code Playgroud)
但是x是一个constexpr! x在编译时是已知的5.为什么MSVC会对此大做文章?(是不是又另一个MSVC的错误?)GCC将愉快地编译它.
在我看来,在MSVC(版本15.7.3)中评估另一个constexpr-if语句的废弃分支内的constexpr-if语句.
请考虑以下代码:
#include <tuple>
#include <type_traits>
template <size_t I>
int test() {
if constexpr(I != 0) {
return 0;
}
else { // This branch is discarded, but it seems that the constexpr-if below is still evaulated?
if constexpr(std::is_same_v<int, std::tuple_element_t<I, std::tuple<int>>>) { // some constexpr check that is valid only when I == 0
return 1;
}
else {
return 2;
}
}
}
int main() {
test<1>();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
上面的代码无法在MSVC中编译,因为std::tuple_element_t当I超过元组的边界时,静态断言将失败.这表明,不管怎样,废弃分支中的代码也会被评估,即使它依赖于模板参数I.
在 C++20 中,<algorithm>头文件获得了两种新算法:shift_left()和shift_right(). 它们都接受任何 LegacyForwardIterator。对于shift_left(),指定“以i从?0”开始的递增顺序执行移动;对于shift_right(),指定“如果ForwardIt满足 LegacyBidirectionalIterator 要求,则按照从i开始的递减顺序执行移动last - first - n - 1”。
我可以想到一个相当简单的实现方法shift_left():
template <typename ForwardIt>
constexpr inline ForwardIt shift_left(ForwardIt first, ForwardIt last, typename std::iterator_traits<ForwardIt>::difference_type n) {
if (n <= 0) return last;
ForwardIt it = first;
for (; n > 0; --n, ++it) {
if (it == last) return first;
}
return std::move(it, last, first);
} …Run Code Online (Sandbox Code Playgroud) 请考虑以下代码(在C++ 11中):
int a = -11, b = 3;
int c = a / b;
// now c == -3
Run Code Online (Sandbox Code Playgroud)
C++ 11规范称负股息的除法向零舍入.
对于有一个运算符或函数来进行除向负无穷大的舍入非常有用(例如,为了在迭代范围时与正红利保持一致),那么标准库中是否有一个函数或运算符可以满足我的需要?或者也许是在现代编译器中执行它的编译器定义的函数/内在函数?
我可以编写自己的,例如以下(仅适用于正数除数):
int div_neg(int dividend, int divisor){
if(dividend >= 0) return dividend / divisor;
else return (dividend - divisor + 1) / divisor;
}
Run Code Online (Sandbox Code Playgroud)
但它不会像我的意图那样描述,也可能不是标准库函数或编译器内在优化(如果存在).
在C++ 17中,我们有std::void_t,这使得SFINAE看起来更好:
template <typename T>
std::void_t<decltype(T::prop)> foo() { /* stuff */ }
Run Code Online (Sandbox Code Playgroud)
模板函数仅在T::prop存在时才存在.
如果T::prop存在,模板函数foo()将等效于:
template <typename T>
void foo() { /* stuff */ }
Run Code Online (Sandbox Code Playgroud)
否则,代码相当于根本没有声明foo().
是否std::void_t对标准库中的其他类型进行了泛化,例如:
template<typename T, typename...>
using generic_t = T;
Run Code Online (Sandbox Code Playgroud)
以便下面的代码有效?
template <typename T>
std::generic_t<int, decltype(T::prop)> foo() { /* stuff */ }
Run Code Online (Sandbox Code Playgroud)
这相当于
template <typename T>
int foo() { /* stuff */ }
Run Code Online (Sandbox Code Playgroud)
如果T::prop存在?