gcc 使用符号位翻转和加法的浮点减法

jc3wubiy  于 2023-08-06  发布在  其他
关注(0)|答案(2)|浏览(128)

从这个问题Replacing __aeabi_dsub to save space (-flto issues)中提取双减法代码并稍微调整它(对于double和float值):

extern "C" double __aeabi_dsub(double a, double b) {
  // flip top bit of 64 bit number (the sign bit)
  reinterpret_cast<unsigned char *>(&b)[7] ^= 0x80; // assume little endian
  return a + b;
}

extern "C" float __aeabi_fsub(float a, float b) {
  // flip top bit of 32 bit number (the sign bit)
  reinterpret_cast<unsigned char *>(&b)[3] ^= 0x80; // assume little endian
  return a + b;
}

字符串

**这些a - b的实现(对于double/float)是否违反了任何浮点代码/ IEEE规范?**假设ARM Cortex-M0架构不支持浮点,由GCC编译。

flseospp

flseospp1#

假设IEEE 754浮点,这不应该破坏任何代码,这很容易通过查看编译代码来看到。

double dsub1(double a, double b) {
  reinterpret_cast<unsigned char *>(&b)[7] ^= 0x80; // assume little endian
  return a + b;
}

double dsub2(double a, double b) {
  return a - b;
}

字符串
编译为

dsub1(double, double):                             // @dsub1(double, double)
        fsub    d0, d0, d1
        ret
dsub2(double, double):                             // @dsub2(double, double)
        fsub    d0, d0, d1
        ret


https://godbolt.org/z/rY4h5YTqb
正如您所看到的,即使在不允许不兼容的FP转换的低优化级别上,这些也是等效的。

whhtz7ly

whhtz7ly2#

这应该是很好的,至少在概念层面上。但是,你需要在这里小心一点。
减法例程与加法例程大小相同的事实可能意味着(至少)两件事:

  • 图书馆的作家们做得很差;或者是
  • 他们做得很好,但你还没有意识到这一点:-)

我之所以这样说是因为我过去写过多精度整数库,除了一些委托,加法和减法例程可以假设某些属性以简化代码。因此,例如,(伪)代码将类似于(在注解中,+x意味着x >= 0-x means x < 0`):

def add(a, b):
    if a <= 0:
        if b <= 0:
            return -add(-a, -b)      # -a, -b.
        return sub(b, -a)            # -a, +b.

    if b <= 0:
        return sub(a, -b)            # +a, -b.

    # +a, +b, hence greatly simplified code.

def sub(a, b):
    if a <= 0:
        if b <= 0:
            return -sub(-a, -b)      # -a, -b.
        return -add(-a, b)           # -a, +b.

    if b <= 0:
        return add(a, -b)            # +a, -b.

    if a < b:
        return -sub(b, a)            # +a, +b, a < b.

    # +a, +b, a >= b, hence greatly simplified code.

字符串
右边的注解显示了使if条件为真的保证条件。并不是所有这些都被显式检查,因为如果没有它们,早期的if语句将为true,并且w函数将已经返回。
“简化代码”区可以集中精力做好自己的工作,知道它拥有的数字是“安全的”。举例来说:

  • 它可以做加法,知道两个数字都是非负的,所以这是一个简单的事情,从右边开始,用进位来加数字。
  • 它可以做减法,而不必担心第二个数字大于第一个数字,这在简单的实现中会导致“无限借用”问题。

所以,如果你的加法和减法例程基本上是重复的(即库编写者做得很差),没有相互引用(即使是间接地通过其他调用),你可能会通过使用你的方法保存一些空间。
然而,如果库的编写者比这聪明一点,他们很可能已经完成了类似于我上面描述的委托工作。这意味着用你所提议的东西来替换sub是一个相当糟糕的主意:

def sub(a, b):
    return add(a, -b)


这是因为add(5, -1)几乎会将该调用委托给sub(5, 1)。当然,这会将它发送回add(5, -1),依此类推,直到堆栈溢出:-)
所以,在你认为你的方法有效之前,要确保这些委托不会发生。因为这是一个库编写者 * 应该 * 在他们的代码中放入的东西(但请参阅上面的“做得很差”文本)。

相关问题