您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

使用数学的力量来简化多级比较

时间:2022-08-26 11:33:37  来源:网易号  作者:漫漫开发路

今天的标题确实是无聊。

有些时候,你可能需要进行一项多级计算。最为常见的例子是对一个包含有主版本号和次版本号的版本信息进行检查。错误的版本号检查是最常见的错误来源之一。

如果你正在编写代码检查版本数字,则可以使用VerifyVersionInfo这个函数,我在此假设你正在为windows 2000及更新版本的Windows操作系统编写代码。

您可以将待比较的数值打包到单个比较操作中,而不是编写多级比较。我们先考虑下面的代码:
inline unsigned __int64
MakeUINT64(Dword Low, DWORD High)
{
ULARGE_INTEGER Value;
Value.LowPart = Low;
Value.HighPart = High;
return Value.QuadPart;
}
BOOL IsVersionAtLeast(DWORD Major, DWORD Minor,
DWORD MajorDesired, DWORD MinorDesired)
{
return MakeUINT64(Minor, Major) >= MakeUINT64(MinorDesired, MajorDesired);
}
这里发生了什么呢?

我们将2个32位的值合并到一个更大的64位的值中,并将最重要的部分放在高位部分,将不太重要的部分放在低位部分。
然后我们坐下来,让数学的力量为我们工作。 如果你还记得小学的比较规则,你会发现它们完全符合我们要应用于多级比较的规则。 比较主要值; 如果不同,那就是结果。 否则,比较次要值。
如果你还是不信,让我们看看生成的汇编代码:
00000 8b 44 24 04 mov eax, DWORD PTR _Major$[esp-4]
00004 3b 44 24 0c cmp eax, DWORD PTR _MajorDesired$[esp-4]
00008 8b 4c 24 08 mov ecx, DWORD PTR _Minor$[esp-4]
0000c 8b 54 24 10 mov edx, DWORD PTR _MinorDesired$[esp-4]
00010 72 0b jb SHORT $L48307
00012 77 04 ja SHORT $L48317
00014 3b ca cmp ecx, edx
00016 72 05 jb SHORT $L48307
$L48317:
00018 33 c0 xor eax, eax
0001a 40 inc eax
0001b eb 02 jmp SHORT $L48308
$L48307:
0001d 33 c0 xor eax, eax
$L48308:
0001f c2 10 00 ret 16 ; 00000010H

编译器生成的代码等价于下面的代码:
BOOL IsVersionAtLeastEquiv(DWORD Major, DWORD Minor,
DWORD MajorDesired, DWORD MinorDesired)
{
if (Major < MajorDesired) return FALSE; if (Major > MajorDesired) return TRUE;
if (Minor < MinorDesired) return FALSE; return TRUE; } 实际上,如果你以(容易出错的)老式方式编写代码,你会得到: BOOL IsVersionAtLeast2(DWORD Major, DWORD Minor, DWORD MajorDesired, DWORD MinorDesired) { return Major > MajorDesired ||
(Major == MajorDesired && Minor >= MinorDesired);
}
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 8b 45 08 mov eax, DWORD PTR _Major$[ebp]
00006 3b 45 10 cmp eax, DWORD PTR _MajorDesired$[ebp]
00009 77 0e ja SHORT $L48329
0000b 75 08 jne SHORT $L48328
0000d 8b 45 0c mov eax, DWORD PTR _Minor$[ebp]
00010 3b 45 14 cmp eax, DWORD PTR _MinorDesired$[ebp]
00013 73 04 jae SHORT $L48329
$L48328:
00015 33 c0 xor eax, eax
00017 eb 03 jmp SHORT $L48330
$L48329:
00019 33 c0 xor eax, eax
0001b 40 inc eax
$L48330:
0001c 5d pop ebp
0001d c2 10 00 ret 16 ; 00000010H

如你所见,这在功能上与之前的两个版本相同。

你也可以将值打包成更小的单位,前提是你知道不会出现溢出或截断。 例如,如果你知道 Major 和 Minor 值永远不会超过 65535,则可以使用以下代码:
BOOL SmallIsVersionAtLeast(WORD Major, WORD Minor,
WORD MajorDesired, WORD MinorDesired)
{
return MAKELONG(Minor, Major) >= MAKELONG(MinorDesired, MajorDesired);
}
00000 0f b7 44 24 0c movzx eax, WORD PTR _MajorDesired$[esp-4]
00005 0f b7 4c 24 10 movzx ecx, WORD PTR _MinorDesired$[esp-4]


0000a 0f b7 54 24 08 movzx edx, WORD PTR _Minor$[esp-4]
0000f c1 e0 10 shl eax, 16 ; 00000010H
00012 0b c1 or eax, ecx
00014 0f b7 4c 24 04 movzx ecx, WORD PTR _Major$[esp-4]
00019 c1 e1 10 shl ecx, 16 ; 00000010H
0001c 0b ca or ecx, edx
0001e 33 d2 xor edx, edx
00020 3b c8 cmp ecx, eax
00022 0f 9d c2 setge dl
00025 8b c2 mov eax, edx
00027 c2 10 00 ret 16 ; 00000010H

如果你知道版本永远不会超过 255,那么你可以做得更小:
BOOL TinyIsVersionAtLeast(BYTE Major, BYTE Minor,
BYTE MajorDesired, BYTE MinorDesired)
{
return MAKEWORD(Minor, Major) >= MAKEWORD(MinorDesired, MajorDesired);
}
00000 33 c0 xor eax, eax
00002 8a 64 24 0c mov ah, BYTE PTR _MajorDesired$[esp-4]
00006 33 c9 xor ecx, ecx
00008 8a 6c 24 04 mov ch, BYTE PTR _Major$[esp-4]
0000c 8a 44 24 10 mov al, BYTE PTR _MinorDesired$[esp-4]
00010 8a 4c 24 08 mov cl, BYTE PTR _Minor$[esp-4]
00014 66 3b c8 cmp cx, ax
00017 1b c0 sbb eax, eax
00019 40 inc eax
0001a c2 10 00 ret 16 ; 00000010H

如果原始版本可以正常工作,我们为什么要改它呢? 因为你可能想要进行三向或四向比较,并且将值包装得更小可以让你将更多数值放入比较代码中。
BOOL IsVersionBuildAtLeast(
WORD Major, WORD Minor, DWORD Build,
WORD MajorDesired, WORD MinorDesired, DWORD BuildDesired)
{
return MakeUINT64(Build, MAKELONG(Minor, Major)) >=
MakeUINT64(Build, MAKELONG(MinorDesired, MajorDesired));
}

通过将主要版本、次要版本和内部版本号打包成一个 64 位值,一个比较操作将同时比较所有三个。 将此与你通常必须编写的复杂(并且代码难以理解)的比较链进行比较:
return Major > MajorDesired ||
(Major == MajorDesired &&
(Minor >= MinorDesired ||
(Minor == MinorDesired && Build >= BuildDesired)));

总结

手工撸代码固然香,但机器的时代来临了:机器的事情,请交给机器。
我们可以将时间花费在设计上,这事儿,机器它干不了。

最后

Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《Using the powers of mathematics to simplify multi-level comparisons》



Tags:多级比较   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
使用数学的力量来简化多级比较
今天的标题确实是无聊。有些时候,你可能需要进行一项多级计算。最为常见的例子是对一个包含有主版本号和次版本号的版本信息进行检查。错误的版本号检查是最常见的错误来源之...【详细内容】
2022-08-26  Search: 多级比较  点击:(381)  评论:(0)  加入收藏
▌简易百科推荐
Netflix 是如何管理 2.38 亿会员的
作者 | Surabhi Diwan译者 | 明知山策划 | TinaNetflix 高级软件工程师 Surabhi Diwan 在 2023 年旧金山 QCon 大会上发表了题为管理 Netflix 的 2.38 亿会员 的演讲。她在...【详细内容】
2024-04-08    InfoQ  Tags:Netflix   点击:(2)  评论:(0)  加入收藏
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(7)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(13)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(9)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(5)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(11)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(9)  评论:(0)  加入收藏
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Java技术指北  微信公众号  Tags:HashMap   点击:(11)  评论:(0)  加入收藏
如何从头开始编写LoRA代码,这有一份教程
选自 lightning.ai作者:Sebastian Raschka机器之心编译编辑:陈萍作者表示:在各种有效的 LLM 微调方法中,LoRA 仍然是他的首选。LoRA(Low-Rank Adaptation)作为一种用于微调 LLM(大...【详细内容】
2024-03-21  机器之心Pro    Tags:LoRA   点击:(12)  评论:(0)  加入收藏
这样搭建日志中心,传统的ELK就扔了吧!
最近客户有个新需求,就是想查看网站的访问情况。由于网站没有做google的统计和百度的统计,所以访问情况,只能通过日志查看,通过脚本的形式给客户导出也不太实际,给客户写个简单的...【详细内容】
2024-03-20  dbaplus社群    Tags:日志   点击:(4)  评论:(0)  加入收藏
相关文章
    无相关信息
站内最新
站内热门
站内头条