在哪里可以找到有关 avr-gcc 在为 8 位处理器进行编译时使用的浮点值的精确二进制表示形式的信息?

问题描述 投票:0回答:3

我需要在使用 Arduino 框架使用 Platformio 为 Atmega328 构建的 C++ 项目中找出

float
double
的准确二进制表示。我无法访问实际硬件,因此无法亲自检查。

微控制器没有 FPU 并且是 8 位的,因此它几乎完全取决于编译器(或框架的库?) - 在本例中似乎是

avr-gcc
,版本 7.3。我已经设法了解
avr-gcc
文档
告诉我,默认情况下
double
的表示方式与
float
相同,但没有指定实际是什么(仅提到 IEEE 标准)可选
long double
)。

所以,这个问题实际上是双重的。最重要的是,我需要知道在这种特殊情况下浮点数的表示形式是什么(我强烈怀疑它是 IEEE 754,但可以使用确认)。其次,我想知道在哪里可以正式找到这些信息,作为某种官方文档的一部分。

c++ floating-point avr-gcc
3个回答
1
投票

浮点格式

无论如何,浮点格式是:

IEEE-754,二进制,小端。 另请参阅 avr-gcc Wiki:类型布局

在编码形式中,表示的各个部分将占据:

32 位浮点 64 位浮点
签名 1 位(31) 1 位 (63)
有偏指数 8 位 (30−23) 11 位 (62−52)
编码尾数 23 位 (22−0) 52 位 (51−0)
指数偏差 127 1023
尺寸 4 8

NaN 是无信号的。

一些属性可用作 GCC 内置宏,例如

float
、run

> echo "" | avr-gcc -xc - -E -dM | grep _FL | sort

#define __FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__
...
#define __FLT_HAS_DENORM__ 1
#define __FLT_HAS_INFINITY__ 1
#define __FLT_HAS_QUIET_NAN__ 1
#define __FLT_MANT_DIG__ 24
#define __FLT_MAX_EXP__ 128
...
#define __FLT_MIN_EXP__ (-125)
#define __FLT_RADIX__ 2
#define __SIZEOF_FLOAT__ 4

对于

double
属性,grep 查找
__DBL
DOUBLE

浮点可用性

  • 直到(包括avr-gcc v9),我们有

    float
    =
    double
    =
    long double
    并且全部都是 32 位宽。

  • 对于 avr-gcc v10 及以上版本:

    double
    的大小取决于命令行选项
    -mdouble=[32|64]
    ,参见。 avr-gcc 命令行选项。此选项的默认值和可用性取决于配置选项
    --with-double=...
    ,参见。 AVR 后端的 GCC 配置选项

    类似的情况也适用于
    long double
    -mlong-double=
    --with-long-double=

  • 浮点库不支持精简的微型内核 (

    -mmcu=avrtiny
    )。

  • 对于不支持
  • MUL

    指令的设备,

    64 位浮点支持不完整。

浮点实现

  • 对于主机上的计算(如常量折叠),GCC 使用MPFR

  • AVR 目标的 32 位浮点作为 AVR-LibC 的一部分实现,甚至是您通常在 libgcc 中期望的部分。

  • AVR 目标的 64 位浮点作为 libgcc 的一部分实现,甚至是您通常在 libm 中期望的部分。

  • 某些功能可能不 100% 符合 IEEE 标准。 例如,IEEE 要求像

    sin
    这样的函数的结果就像以无限精度计算
    sin
    一样,然后根据所选的舍入模式进行舍入。 出于效率考虑,某些函数返回的结果的精度可能低于 IEEE 规定的精度。


0
投票
如果没有硬件浮点,则浮点支持由平台的

libc 完成。在 avr-gcc 中,我在文档中也看不到任何有关 float

 格式的信息。要真正确认它,您确实需要硬件或阅读 avr-gcc 源代码

但是您可以快速检查浮点常量,看看它们是否与 IEEE-754 值匹配。如果是,那么格式很可能是 IEEE-754。您还可以检查

__STDC_IEC_559__

 是否完全符合 IEEE-754 要求

#include <stdio.h> #include <limits.h> #include <float.h> int main(void) { #ifdef __STDC_IEC_559__ puts("\n__STDC_IEC_559__ macro defined\n"); #else puts("\n__STDC_IEC_559__ macro not defined\n"); #endif char flt_dig = FLT_DIG; // should be 6 char flt_mant_dig = FLT_MANT_DIG; // should be 24 char flt_max_10_exp = FLT_MAX_10_EXP; // should be 38 int flt_max_exp = FLT_MAX_EXP; // should be 128 char flt_min_10_exp = FLT_MIN_10_EXP; // should be -37 char flt_min_exp = FLT_MIN_EXP; // should be -125 return 0; }

在Godbolt中编译上述代码,并检查相应的装配线,其颜色与原始源代码相同。很容易看出这些值与预期的 IEEE-754 二进制 32 相同。但它并不完全符合 IEEE 754 标准


0
投票
我写了两个测试文件。一个用于 AVR(

test.c):

float test_u_float = 0.1234f, test_s_float = -0.1234f; double test_u_double = 0.1234, test_s_double = -0.1234;
这是为Atmega640编译的:

avr-gcc -x c -mmcu=atmega640 -O0 -save-temps test.c
并产生了这个输出(

test.s):

.file "test.c" __SP_H__ = 0x3e __SP_L__ = 0x3d __SREG__ = 0x3f __tmp_reg__ = 0 __zero_reg__ = 1 .text .global test_u_float .data .type test_u_float, @object .size test_u_float, 4 test_u_float: .byte 36 .byte -71 .byte -4 .byte 61 .global test_s_float .type test_s_float, @object .size test_s_float, 4 test_s_float: .byte 36 .byte -71 .byte -4 .byte -67 .global test_u_double .type test_u_double, @object .size test_u_double, 4 test_u_double: .byte 36 .byte -71 .byte -4 .byte 61 .global test_s_double .type test_s_double, @object .size test_s_double, 4 test_s_double: .byte 36 .byte -71 .byte -4 .byte -67 .ident "GCC: (GNU) 11.2.0" .global __do_copy_data
还为

amd64平台编写并编译了一个简单的程序:

#include <stdio.h> float foo = 0.1234f, bar = -0.1234f; int main(void) { printf("U %08X S %08X\n", *(unsigned*)&foo, *(unsigned*)&bar); return 0; }
产生了这个:

U 3DFCB924 S BDFCB924

test.s 中的有符号字节转换为二进制表明 GCC 在 AVR 上以与 AMD64 相同的格式对浮点数和双精度数进行编码,即 IEEE 754

© www.soinside.com 2019 - 2024. All rights reserved.