我希望dLower
和dHigher
分别具有两个双值的较低和较高值 - 即,如果它们是错误的方式,则对它们进行排序。最直接的答案似乎是:
void ascending(double& dFirst, double& dSecond)
{
if(dFirst > dSecond)
swap(dFirst,dSecond);
}
ascending(dFoo, dBar);
但这似乎是一件显而易见的事情,我想知道我是不是在使用正确的术语来找到标准例程。
另外,你会如何制作这种通用的?
答案 0 :(得分:4)
这是接近它的好方法。它会像你要的那样高效。 我怀疑这个特定的函数有一个公认的名字。这显然叫做比较交换。
在类型上对其进行推广非常简单:
template <typename T>
void ascending(T& dFirst, T& dSecond)
{
if (dFirst > dSecond)
std::swap(dFirst, dSecond);
}
证实这个功能:
int main() {
int a=10, b=5;
ascending(a, b);
std::cout << a << ", " << b << std::endl;
double c=7.2, d=3.1;
ascending(c, d);
std::cout << c << ", " << d << std::endl;
return 0;
}
打印:
5, 10
3.1, 7.2
答案 1 :(得分:3)
玩“非常通用”的游戏:
template <typename T, typename StrictWeakOrdering>
void comparison_swap(T &lhs, T &rhs, StrictWeakOrdering cmp) {
using std::swap;
if (cmp(rhs, lhs)) {
swap(lhs, rhs);
}
}
template <typename T>
void comparison_swap(T &lhs, T &rhs) {
comparison_swap(lhs, rhs, std::less<T>());
}
这会勾选以下方框:
std::greater<T>
作为默认值并相应地进行修改)。它也保证对同一类型的任意指针有效,operator<
不是。std::swap
的特化或ADL找到的swap
函数,以防T类型提供一个但不提供另一个。但可能会有一些我忘记的盒子。
答案 2 :(得分:2)
正如你也问过,
是否有更有效的排序方式 两个数字?
考虑到效率,您可能需要编写自己的交换函数并针对std :: swap测试其性能。
以下是Microsoft实施。
template<class _Ty> inline
void swap(_Ty& _Left, _Ty& _Right)
{ // exchange values stored at _Left and _Right
if (&_Left != &_Right)
{ // different, worth swapping
_Ty _Tmp = _Left;
_Left = _Right;
_Right = _Tmp;
}
}
如果您认为不需要检查条件if (&_Left != &_Right)
,则可以省略它以提高代码的性能。您可以编写自己的交换,如下所示。
template <class T>
inline void swap(T &left, T& right)
{
T temp = left;
left = right;
right = temp;
}
对于我而言,10千万次调用的性能略有改善。 无论如何,您需要正确地测量与性能相关的更改。不要假设。
某些库函数的运行速度可能不如考虑通用用法,错误检查等。如果性能在您的应用程序中并不重要,建议使用库函数,因为它们经过了充分测试。
如果性能像硬实时系统一样至关重要,那么编写自己的系统并使用它是没有错的。
答案 3 :(得分:2)
让我在这里抛出一个特殊情况,只有当性能是一个绝对关键的问题并且浮点精度足够时才适用:你可以考虑矢量管道(如果你的目标CPU有一个)。
有些CPU可以通过每个指令获取向量的每个组件的最小值和最大值,因此您可以一次处理4个值 - 完全没有任何分支。
同样,这是一个非常特殊的案例,很可能与你正在做的事情无关,但我想提出这个问题,因为“更有效率”是问题的一部分。
答案 4 :(得分:1)
为什么不将std :: sort()与lambda或functor一起使用?
答案 5 :(得分:1)
除EboMike专注于编程通用性之外,所有答案均使用相同的底层比较和交换方法。我对这个问题很感兴趣,因为拥有专门的分支避免流水线效率会很好。我正在草拟一些未经测试/未进行基准测试的实现,这些实现可以通过利用条件移动指令(例如cmovl
)来避免分支,从而比以前的答案更有效地进行编译。我不知道这是否体现在实际的性能提升中,但是...
可以通过在通用情况下使用“比较并交换”方法来进行这些专长来增加编程的一般性。这是一个足够普遍的问题,我真的很希望看到它正确地实现为库中一组体系结构调整的专业。
我在注释中包含了godbolt的x86程序集输出。
/*
mov eax, dword ptr [rdi]
mov ecx, dword ptr [rsi]
cmp eax, ecx
mov edx, ecx
cmovl edx, eax
cmovl eax, ecx
mov dword ptr [rdi], edx
mov dword ptr [rsi], eax
ret
*/
void ascending1(int &a, int &b)
{
bool const pred = a < b;
int const _a = pred ? a : b;
int const _b = pred ? b : a;
a = _a;
b = _b;
}
/*
mov eax, dword ptr [rdi]
mov ecx, dword ptr [rsi]
mov edx, ecx
xor edx, eax
cmp eax, ecx
cmovle ecx, eax
mov dword ptr [rdi], ecx
xor ecx, edx
mov dword ptr [rsi], ecx
ret
*/
void ascending2(int &a, int &b)
{
bool const pred = a < b;
int const c = a^b;
a = pred ? a : b;
b = a^c;
}
/*
The following implementation changes to a function-style interface,
which I feel is more elegant, although admittedly always forces assignment
to occur, so will be more expensive if assignment is costly.
See foobar() to see that this rather nicely inlines.
mov eax, esi
mov ecx, esi
xor ecx, edi
cmp edi, esi
cmovle eax, edi
xor ecx, eax
shl rcx, 32
or rax, rcx
ret
*/
std::pair<int,int> ascending3(int const a, int const b)
{
bool const pred = a < b;
int const c = a^b;
int const x = pred ? a : b;
int const y = c^x;
return std::make_pair(x,y);
}
/*
This is to show that ascending3() inlines very nicely
to only 5 assembly instructions.
# inlined ascending3().
mov eax, esi
xor eax, edi
cmp edi, esi
cmovle esi, edi
xor eax, esi
# end of inlining.
add eax, esi
ret
*/
int foobar(int const a, int const b)
{
auto const [x,y] = ascending3(a,b);
return x+y;
}