如何根据 IEEE 754 (ansi-c) 获取双精度数的上/下机器字?

发布于 2024-10-05 05:06:46 字数 1907 浏览 0 评论 0原文

我想使用 fdlibm 的 sqrt 实现。
此实现定义了(根据字节序)一些宏,用于通过以下方式访问双精度型的低/高32位)(此处:仅小端版本)

#define __HI(x) *(1+(int*)&x)
#define __LO(x) *(int*)&x
#define __HIp(x) *(1+(int*)x)
#define __LOp(x) *(int*)x

: flibm 说的是以下内容(稍微缩短了一点)

Each double precision floating-point number must be in IEEE 754 
double format, and that each number can be retrieved as two 32-bit 
integers through the using of pointer bashing as in the example 
below:

Example: let y = 2.0
double fp number y:     2.0
IEEE double format: 0x4000000000000000

Referencing y as two integers:
*(int*)&y,*(1+(int*)&y) =   {0x40000000,0x0} (on sparc)
            {0x0,0x40000000} (on 386)

Note: Four macros are defined in fdlibm.h to handle this kind of
      retrieving:

__HI(x)     the high part of a double x 
        (sign,exponent,the first 21 significant bits)
__LO(x)     the least 32 significant bits of x
__HIp(x)    same as __HI except that the argument is a pointer
        to a double
__LOp(x)    same as __LO except that the argument is a pointer
        to a double

If the behavior of pointer bashing is undefined, one may hack on the 
macro in fdlibm.h.

我想将此实现和这些宏与 cbmc 一起使用 模型检查器,应符合ansi-c
我不知道到底出了什么问题,但下面的示例显示这些宏不起作用(选择了小端,选择了 32 位机器字):

temp=24376533834232348.000000l (0100001101010101101001101001010100000100000000101101110010000111)
high=0                         (00000000000000000000000000000000)
low=67296391                   (00000100000000101101110010000111)

两者似乎都是错误的。对于每个 temp 值来说,High 似乎都是空的。

使用 ansi-c 访问这两个 32 个单词有什么新想法吗?

更新: 感谢您的所有回答和评论。你所有的建议都对我有用。目前我决定使用“R..”的版本并将其标记为最喜欢的答案,因为它似乎是我的工具中关于字节序的最强大的。

i want to use the sqrt implementation of fdlibm.
This implementation defines (according to the endianess) some macros for accessing the lower/upper 32-bit of a double) in the following way (here: only the little-endian-version):

#define __HI(x) *(1+(int*)&x)
#define __LO(x) *(int*)&x
#define __HIp(x) *(1+(int*)x)
#define __LOp(x) *(int*)x

The readme of flibm is saying the following (a little bit shortened)

Each double precision floating-point number must be in IEEE 754 
double format, and that each number can be retrieved as two 32-bit 
integers through the using of pointer bashing as in the example 
below:

Example: let y = 2.0
double fp number y:     2.0
IEEE double format: 0x4000000000000000

Referencing y as two integers:
*(int*)&y,*(1+(int*)&y) =   {0x40000000,0x0} (on sparc)
            {0x0,0x40000000} (on 386)

Note: Four macros are defined in fdlibm.h to handle this kind of
      retrieving:

__HI(x)     the high part of a double x 
        (sign,exponent,the first 21 significant bits)
__LO(x)     the least 32 significant bits of x
__HIp(x)    same as __HI except that the argument is a pointer
        to a double
__LOp(x)    same as __LO except that the argument is a pointer
        to a double

If the behavior of pointer bashing is undefined, one may hack on the 
macro in fdlibm.h.

I want to use this implementation and these macros with the cbmc model checker, which should be conformable with ansi-c.
I don't know exactly whats wrong, but the following example shows that these macros aren't working (little-endian was chosen, 32-bit machine-word was chosen):

temp=24376533834232348.000000l (0100001101010101101001101001010100000100000000101101110010000111)
high=0                         (00000000000000000000000000000000)
low=67296391                   (00000100000000101101110010000111)

Both seem to be wrong. High seems to be empty for every value of temp.

Any new ideas for accessing the both 32-words with ansi-c?

UPDATE: Thanks for all your answers and comments. All of your proposals worked for me. For the moment i decided to use "R.."s version and marked this as favorite answer because it seems to be the most robust in my tool regarding endianness.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

忱杏 2024-10-12 05:06:46

像您所做的那样转换指针违反了 C 语言的别名规则(编译器可能会假定不同类型的指针不指向相同的数据,除非在某些非常有限的情况下)。更好的方法可能是:

#define REP(x) ((union { double v; uint64_t r; }){ x }).r
#define HI(x) (uint32_t)(REP(x) >> 32)
#define LO(x) (uint32_t)(REP(x))

请注意,这还修复了字节序依赖性(假设浮点和整数字节序相同)和宏名称上的非法 _ 前缀。

更好的方法可能是根本不将其分成高/低部分,并直接使用 uint64_t 表示 REP(x)

从标准的角度来看,联合的这种使用有点可疑,但比指针强制转换更好。使用强制转换为 unsigned char * 并逐字节访问数据在某些方面会更好,但更糟糕的是您必须担心字节序考虑,并且可能会慢很多。

Casting pointers like you're doing violates the aliasing rules of the C language (pointers of different types may be assumed by the compiler not to point to the same data, except in certain very restricted cases). A better approach might be:

#define REP(x) ((union { double v; uint64_t r; }){ x }).r
#define HI(x) (uint32_t)(REP(x) >> 32)
#define LO(x) (uint32_t)(REP(x))

Note that this also fixed the endian dependency (assuming the floating point and integer endianness are the same) and the illegal _-prefix on the macro names.

An even better way might be not breaking it into high/low portions at all, and using the uint64_t representation REP(x) directly.

From a standards perspective, this use of unions is a little bit suspect, but better than the pointer casts. Using a cast to unsigned char * and accessing the data byte-by-byte would be better in some ways, but worse in that you have to worry about endian considerations, and probably a lot slower..

北座城市 2024-10-12 05:06:46

为什么不使用工会?

union {
    double value;
    struct {
        int upper;
        int lower;
    } words;
} converter;

converter.value = 1.2345;
printf("%d",converter.words.upper);

(请注意,行为代码依赖于实现,并且依赖于内部表示和特定数据大小)

最重要的是,如果使该结构包含位字段,则可以访问各个浮点部分(符号、指数和尾数)分别:

union {
    double value;
    struct {
        int upper;
        int lower;
    } words;
    struct {
        long long mantissa : 52; // not 2C!
        int exponent : 11;       // not 2C!
        int sign : 1;
    };        
} converter;

Why not use an union?

union {
    double value;
    struct {
        int upper;
        int lower;
    } words;
} converter;

converter.value = 1.2345;
printf("%d",converter.words.upper);

(Note that the behaviour code is implementation-dependent and relies on internal representation and specific data sizes)

On top of that, if you make that struct contain bitfields, you can access the individual floating-point parts (sign, exponent and mantissa) separately:

union {
    double value;
    struct {
        int upper;
        int lower;
    } words;
    struct {
        long long mantissa : 52; // not 2C!
        int exponent : 11;       // not 2C!
        int sign : 1;
    };        
} converter;
坦然微笑 2024-10-12 05:06:46

我建议看一下反汇编,看看为什么现有的“指针攻击”方法不起作用。如果没有它,您可能会使用更传统的方法,例如二进制移位(如果您使用的是 64 位系统)。

I would suggest taking a look at the disassembly to see exactly why the existing "pointer-bashing" method does not work. In its absence, you might use something more traditional like a binary shift (if you're on a 64-bit system).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文