【问题标题】:Separate a double into it's sign, exponent and mantissa将双精度数分成符号、指数和尾数
【发布时间】:2013-09-12 02:04:38
【问题描述】:

我已经阅读了一些已经分解双打并“将其组合在一起”的主题,但我正在尝试将其分解为它的基本组件。到目前为止,我已经确定了一点:

breakDouble( double d ){

    long L = *(long*) &d;

    sign;
    long mask = 0x8000000000000000L;

    if( (L & mask) == mask ){

        sign = 1;

    } else {

        fps.sign = 0;
    }
    ...
}

但我对如何获得指数和尾数感到很困惑。我没有强迫双倍长,因为只有前导位很重要,所以截断没有发挥作用。但是,对于其他部分,我认为这不会起作用,而且我知道您不能对浮点数执行按位运算符,所以我被卡住了。

想法?


编辑:当然,我一发帖就找到this, but I'm not sure how different floats and doubles are in this case.


编辑 2(抱歉,我正在工作): 我阅读了我在编辑 1 中链接的那篇文章,在我看来,我可以以同样的方式执行他们在我的替身上所做的操作,带有掩码的指数是:

mask = 0x7FF0000000000000L;

对于尾数:

mask = 0xFFFFFFFFFFFFFL;

这对吗?

【问题讨论】:

  • L 中有位,因此您可以对其进行位操作。得到你想要的只是掩饰和转移的问题。我忘记了偏移量和偏移量是什么。
  • 是的,但我不是通过将替身的准确性“破坏”为长吗?
  • 嗯,已经有一个函数可以做到这一点:frexp.
  • @RaymondChen:frexp 做的事情略有不同。当出现 NaN、无穷大和次正规时,它的行为是合理的,因此它比 bit-hacky 方式要慢。

标签: c double


【解决方案1】:

您在第二次编辑中发布的位掩码看起来不错。但是,您应该注意:

  1. 像你这样取消引用 (long *)&mydouble 违反了 C 的别名规则。如果你传递像 gcc 的 -fno-strict-aliasing 这样的标志,这仍然会在大多数编译器下运行,但如果你不这样做,它可能会导致问题。您可以转换为 char * 并以这种方式查看这些位。它更烦人,您必须担心字节顺序,但您不会冒编译器搞砸一切的风险。您还可以创建一个联合类型,如帖子底部的那个,并写入d 成员,同时读取其他三个成员。

  2. 较小的可移植性说明:long 的大小在任何地方都不相同;也许尝试改用uint64_t? (double 也不是,但很明显这仅适用于 IEEE doubles。)

  3. 位掩码的诡计仅适用于所谓的“正常”浮点数 --- 那些具有既不为零(表示次正规)或 2047(表示无穷大或 NaN)的偏置指数。

  4. 正如 Raymond Chen 所指出的,frexp 函数可以满足您的实际需要。 frexp 以有记录且理智的方式处理次正规、无穷大和 NaN 情况,但使用它会影响速度。

(显然,列表和代码块之间需要一些非列表文本。在这里;吃掉它,markdown!)

union doublebits {
  double d;
  struct {
    unsigned long long mant : 52;
    unsigned int expo : 11;
    unsigned int sign : 1;
  };
};

【讨论】:

  • 我打算使用 math.h 中包含的 isnan 和 isinf 函数来处理边缘情况。我不知道如何处理-0。至于#2,一旦我让它运行起来,我会尝试让它更便携,谢谢你的提示。我知道,但有时很容易忘记。
  • @Joshua:那么只需使用math.h 中的frexp 函数即可。当你知道你只有正常的数字时,这种事情对于快速破解很有好处。
  • 这可能是我最终会做的事情,但既然我发了这个帖子,我会弄清楚并为未来的询问者发布答案:)
  • “联合黑客”不是黑客; C 标准规定,访问除最后一个存储的联合成员之外的联合成员会重新解释新类型中的字节。
  • 请注意,在联合中使用 uint64_t 比使用位域结构更适用于 C 实现。位字段更容易出现字节序/顺序问题,而uint64_t 通常与double 具有预期关系:高位是符号位,接下来的 11 个是指数字段,低位是有效字段。使用位域时,应该确保它们按需要排列,可能使用预处理器宏来测试编译器(例如,__GNUC__)和字节序(例如,__LITTLE_ENDIAN__)。
猜你喜欢
  • 2017-03-08
  • 2012-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-07
  • 2011-02-01
相关资源
最近更新 更多