浮点数减法使用符号位翻转和加法。

huangapple go评论96阅读模式
英文:

Floating point subtraction using sign bit flip and add

问题

这些 doublefloat 的减法实现是否违反任何浮点代码/IEEE规范?假设在不具备浮点支持的ARM Cortex-M0架构上,由GCC编译。

请注意,以下是对您提供的代码的翻译,不包括问题的回答:

这些 doublefloat 的减法实现是否违反任何浮点代码/IEEE规范?假设在不具备浮点支持的ARM Cortex-M0架构上,由GCC编译。

英文:

Taking double subtraction code from this question https://stackoverflow.com/q/52428872/1116364 and adjusting it slightly (both for double and float values):

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;
}

Do these implementations of a - b (for double/float) break any floating point code / IEEE specifications? Assuming an ARM Cortex-M0 architecture without floating point support, compilation by GCC.

答案1

得分: 3

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

double dsub1(double a, double b) {
  reinterpret_cast<unsigned char*>(&b)[7] ^= 0x80; // 假设小端字节序
  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转换。

英文:

Assuming IEEE 754 floating point, this shouldn't break any code which is easy to see by looking at the compiled code.

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

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

is compiled to

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)

As you can see these are equivalent even on a low optimization level that doesn't allow incompatible FP transformations.

答案2

得分: 0

应该 是可以的,至少在概念层面上是这样。但是,在这里需要稍微小心一些。

减法函数的大小与加法函数大致相同可能意味着(至少)两种情况:

  • 库的编写者做得不好;或者
  • 他们做得很好,但你可能还没有意识到 浮点数减法使用符号位翻转和加法。

我之所以提到这一点,是因为我过去曾经编写过多精度整数库,除了一些委托之外,加法和减法函数可以假设某些属性以便于简化代码。因此,例如(在注释中,+x 表示 x >= 0-x 表示 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, 因此代码大大简化。

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, 因此代码大大简化。

右侧的注释显示了使 if 条件为真的保证条件。并不是所有这些条件都会明确检查,因为如果没有它们,早期的 if 语句就会为真,函数已经返回了。

然后,“简化的代码”区域可以集中精力执行其任务,因为它知道它拥有的数字是“安全的”。例如:

  • 它可以执行加法,知道这两个数字都是非负的,因此只需要从右边开始添加带有进位的数字。
  • 它可以执行减法,无需担心第二个数字大于第一个数字,这在简单实现中会导致“无限借位”问题。

因此,如果你的加法和减法函数 基本上是重复的(即,库的编写者做得不好),而且没有引用彼此(即使是通过其他调用间接引用),那么你可能可以通过使用你的方法来节省一些空间。

但是,如果库的编写者比这聪明一些,那么他们可能已经做了我上面描述的类似委托的工作。这意味着用你提出的方法替换 sub 将是一个相当 糟糕的 主意:

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

这是因为 add(5, -1) 几乎会将该调用委托给 sub(5, 1)。这当然会将其发送回 add(5, -1),如此反复,直到你的堆栈溢出 浮点数减法使用符号位翻转和加法。

所以,在假设你的方法有效之前,一定要确保这些委托 不会 发生。因为这是一个库编写者应该放入他们的代码中的事情(但请参见上面的“做得不好”文本)。

英文:

That should be fine, at least on a conceptual level. However, you need to be a little bit careful here.

The fact that the subtract routine is about the same size as the add routine could mean (at least) two things:

  • the library writers did a poor job; or
  • they did a good job but you don't yet realise it 浮点数减法使用符号位翻转和加法。

The reason I state this is because I've written multi-precision integer libraries in the past where, other than some delegation, the add and subtract routines could assume certain properties to allow for simplified code. So, for example, the (pseudo-) code would be something like (in the comments, +x means x &gt;= 0, -x means x < 0`):

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

    if b &lt;= 0:
        return sub(a, -b)            # +a, -b.

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

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

    if b &lt;= 0:
        return add(a, -b)            # +a, -b.

    if a &lt; b:
        return -sub(b, a)            # +a, +b, a &lt; b.

    # +a, +b, a &gt;= b, hence greatly simplified code.

The comments to the right show the guaranteed conditions which make the if condition true. Not all these are explicitly checked since, without them, an earlier if statement would have been true and thew function would already have returned.

The "simplified code" area could then concentrate on doing its job knowing that the numbers it had were "safe". For example:

  • It could do addition knowing that both numbers were non-negative, so it's a simple matter of starting at the right and adding digits with carry.
  • It could do subtraction without having to worry that the second number was bigger than the first, something that results in an "infinite borrow" problem in naive implementations.

So, if your add and subtract routines are basically duplicates (i.e., the library writers did a poor job) without referencing each other (even indirectly through other calls), you will probably be able to save some space by using your method.

However, if the library writers were a bit cleverer than that, it may well be that they've done a delegation job similar to what I describe above. That means it would be a rather bad idea to replace sub with something like what you are proposing:

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

That's because add(5, -1) would almost delegate that call to sub(5, 1). Which would, of course, send it back to add(5, -1), and so on, right up until the point your stack overflows 浮点数减法使用符号位翻转和加法。

So, just be certain that these delegations do not happen before you assume that your method will work. Because this is the sort of thing a library writer should have put in their code (but see the "did a poor job" text above).

huangapple
  • 本文由 发表于 2023年7月11日 06:29:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76657709.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定