我目前正在研究嵌入式微控制器并使用自定义printf例程。工具链是AVR32架构的GCC工具链。
我遇到的问题是,在第二次调用vsnprintf或类似的情况下,CPU进入异常情况。
从支持,我收到了答案:
我们找不到任何这种行为的明显原因。但是,通过逐字节写入来创建浮点溢出条件并不安全。我们无法确保由此产生的值,建议使用“FLT_MAX”进行检查。
现在我想知道:什么是“非法”浮动值?不是所有的位组合都不应该代表至少一些值吗?如果相关:sizeof(float)是4个字节。
答案 0 :(得分:5)
我建议你打印浮点值的位,好像它们是一个十六进制整数,如下面的代码所示,这样你就可以分析这些位,看它们是否包含你试图计算或已经计算的值由于某些错误而修改不当。
AVR32CU Technical Reference Manual表示“浮点硬件符合C标准的要求,该标准基于IEEE 754浮点标准。”后一条款为假; C标准不是基于IEEE 754.C标准确实指定了对IEEE 754的绑定(通过名称IEC 60559)作为C实现的可选特征。我假设你使用的AVR32 CPU型号在某种程度上符合IEEE 754。
IEEE 754中没有“非法”值。有些值不代表数字,其中一些值旨在导致异常。这样的值称为NaN(对于“非数字”)。有安静的NaNs和信号NaNs。安静的NaN旨在静默地传递操作,产生NaN结果。例如,3 + NaN
应该产生NaN
。信令NaN旨在导致异常,这可能导致程序控制的更改(例如信号或程序中止)。
上面引用的技术参考手册也说“未提供信令NaN,所有NaN都是非信令(安静)。”
一个好的vsnprintf
例程应该接受安静的NaN值进行打印,并且应该通过生成诸如“NaN”之类的字符串来格式化它们。当传递信令NaN进行格式化时,我认为格式化它或产生异常可能是合理的。
我希望您从支持部门收到的消息表明您的软件创建了某种NaN,vsnprintf
无法处理这些消息。从措词来看,我认为他们的反应是推测性的。
如果您通过汇编字节来创建浮点值,那么如果您的软件中存在某些错误,则可能在您不打算创建NaN时创建了NaN。我建议您通过使用vsnprintf
来打印浮点值的字节,而不是使用浮点格式说明符打印它。
如果您使用的GCC版本具有GCC的常用功能,并且实现中的unsigned int
为32位,则可以格式化32位float
值{{1使用:
x
第二行使用复合文字将值vsnprintf(Buffer, BufferLength, "0x%x",
(union { float f; unsigned int u; }) {x} .u);
放入联合并将其字节重新解释为x
。 (这是C中一种支持的方式来重新解释对象的字节。许多人使用指针别名,如果使用了适当的标志,它在GCC中工作,但C标准通常不支持它。另一种支持的方法是复制字节,与unsigned int
一样。)
一旦看到unsigned int u; memcpy(&u, &x, sizeof u);
中的位数,您就可以从IEEE 754标准中的信息或使用an online analyzer手动解释它们。 (选择“十六进制”按钮输入要解释的十六进制值。)
在IEEE-754 32位二进制浮点对象中,如果出现以下值,则该值为NaN:
(如果位30到23都是1,但位22到0全为零,则该值为无穷大。这不是非法的,但也可能导致低质量float
生成异常。)
答案 1 :(得分:2)
我没有在AVR32上工作,但是'非法'浮点值,通常是浮点数(单精度算术)是数值方法中的重要主题。 float的最大数字是:
FLT_MAX = 3.40282e+38
但浮动也限制浮动值。你越接近零,你可以指定更多的浮动数字。
例如:
[1,2]之间的最小值是1.19209e-07
(它是2 ^ -23),也称为macheps(机器epsilon(来自float.h的FLT_EPSILON))
[2,4]之间的最小值为2 * 1.19209e-07
= 2 * 2^-23
它也适用于奥德方面:
[1 / 2,1]之间的最小值是2^-24
。
为什么会这样?
我们将数字定义为beforedot.afterdot
。
数字越大,写入beforedot
数字需要更多位,而少数字则需要更多位。
总结:
最小浮动:1.0842e-19
,
浮动最大值:3.40282e+38
。