algorithm - 数学上找到最接近 0 的值
问题描述
有没有办法在数学上确定一个值是否比另一个值更接近 0?
例如closerToZero(-2, 3)
会返回-2
。
我尝试删除符号,然后比较最小值的值,但我会分配初始数字的无符号版本。
a 和 b 是符合 IEEE-754 的浮点双精度数(js 编号)
(64 位 => 1 位符号 11 位指数 52 位小数)
min (a,b) => b-((a-b)&((a-b)>>52));
result = min(abs(a), abs(b));
// result has the wrong sign ...
解决方案
显而易见的算法是比较绝对值,并使用它来选择原始值。
如果这绝对需要无分支(例如为了加密安全),请小心使用? :
三元。它通常编译为无分支汇编,但这并不能保证。(我认为这就是您标记分支预测的原因?如果只是出于性能考虑,编译器通常会做出正确的决定。)
在具有 2 的补码整数的固定语言中,请记住abs(INT_MIN)
溢出与输入宽度相同的有符号结果。 在 C 和 C++ 中,abs()
不方便地设计为返回 anint
并且在 2 的补码系统上用最负的 2 的补码整数调用它是未定义的行为。在具有良好定义的包装有符号整数数学的系统上(如gcc -fwrapv
,或者可能是 Java),有符号abs(INT_MIN)
将溢出回 INT_MIN,如果您进行有符号比较,则会给出错误的结果,因为 INT_MIN 最大程度地远离 0。
确保对结果进行无符号比较,abs
以便正确处理INT_MIN
. (或者正如@kaya3 建议的那样,将正整数映射到负数,而不是从负数到正数。)
避免未定义行为的安全 C 实现:
unsigned absu(int x) {
return x<0? 0U - x : x;
}
int minabs(int a, int b) {
return absu(a) < absu(b) ? a : b;
}
请注意,<
vs.<=
实际上很重要minabs
:如果它们的大小相等,则决定选择哪一个。
0U - x
在从 0 中减去可能溢出的之前转换x
为。将负有符号整数类型转换为无符号在 C 和 C++ 中被明确定义为模减少(与浮点数不同,UB IIRC)。在 2 的补码机器上,这意味着使用相同的位模式不变。unsigned
这对于 x86-64 ( Godbolt ) 编译得很好,尤其是对于 clang。(GCCcmov
甚至避免使用-march=skylake
,以更糟糕的序列结束。除了在执行两个 absu 操作之后的最终选择之外,它在 Intel CPU 上使用cmovbe
2 uop 而不是 1 cmovb
,因为它需要读取 ZF 和 CF 标志。如果它已经在 EAX 中以相反的值结束,它可以使用cmovb
.)
# clang -O3
absu:
mov eax, edi
neg eax # sets flags like sub-from-0
cmovl eax, edi # select on signed less-than condition
ret
minabs:
mov ecx, edi
neg ecx
cmovl ecx, edi # inlined absu(a)
mov eax, esi
mov edx, esi
neg edx
cmovl edx, esi # inlined absu(b)
cmp ecx, edx # compare absu results
cmovb eax, edi # select on unsigned Below condition.
ret
GCC 和 clang 完全无分支,启用优化。可以肯定的是,其他 ISA 将是相同的。
它可能会自动矢量化,但 x86 直到 AVX512 才进行 SIMD 无符号整数比较。(您可以通过翻转高位来模拟使用有符号整数pcmpgtd
)。
对于 float / double,abs
更便宜且不会溢出:只需清除符号位,然后使用它来选择原始位。
推荐阅读
- cassandra - 在 Cassandra 中,为什么单个值优先于仲裁节点空响应?
- javascript - 如何在 React 中显示数组?
- vim - 为什么否定字符类不能按预期工作?
- android - SeekBar 无法与来自服务器的流式音频一起正常工作
- python-3.x - Discord.py 将参数添加到 api 命令
- html - 在我学校的网页上,我们希望自动更新专业列表。如何链接领英?
- python - 使用 re.sub() 从匹配的模式中删除空格
- python - 停止执行 python 线程并启动一个新线程
- python - SQL Alchemy - 如何在 PostgreSQL 数据库中使用内置函数?
- apache-nifi - 如何在 NIFI 中从 Onedrive 下载文件