【问题标题】:"Floating-point invalid operation" when inputting float to a stringstream将浮点数输入到字符串流时出现“浮点无效操作”
【发布时间】:2012-05-17 21:46:08
【问题描述】:

我有一段简单的代码,它从 FORTRAN 生成的 REAL 数组中提取浮点数,然后将其插入到流中以进行日志记录。虽然这适用于前 30 个案例,但在 31 日它会因“浮点无效操作”而崩溃。

代码是:

int FunctionDeclaration(float* mrSwap)
{
...
float swap_float;
stringstream message_stream;
...
swap_float = *(mrSwap+30-1);
...
message_stream.clear();
message_stream <<  30 << "\t" << swap_float << "\tblah blah blah \t";

在调试时,实例崩溃前的 swap_float 的值(在上面的最后一行)是 1711696.3 - 除了这比到目前为止的大多数值大得多之外,它并没有什么特别的地方。

我也试过用 cerr 替换 message_stream,也遇到了同样的问题。迄今为止,我一直认为 cerr 几乎是坚不可摧的 - 一个简单的浮子怎么能摧毁它?

编辑:

感谢 cmets:我添加了 mrSwap 的声明。 mrSwap 大约有 200 长,所以我离结束还有很长的路要走。它在我的控制之外填充,并且单个条目可能不会被填充 - 但据我所知,这只是意味着 swap_float 将被设置为随机浮点数?

【问题讨论】:

  • 你能把它做成一个可编译的小例子吗? mrSwap 是什么?它是如何填充的?
  • 听起来你已经超越了终点。
  • 如果这是一个指针操作,我想知道你是否刚刚跑过了数组的末尾。 cerr 没有问题,我敢肯定。
  • 你指的是mrSwap,但不要显示它是什么。
  • 你试过用printf()代替cout吗?

标签: c++ floating-point-exceptions


【解决方案1】:

我只是添加此答案以突出显示上述 TonyK 答案中的正确解决方案 - 因为我们做了几个循环,答案已被编辑,并且由于 cmets 中有几个要点,实际答案可能不会立即出现明显。所有功劳都应归功于 TonyK 的解决方案。

“另一种可能是延迟浮点陷阱。浮点协处理器(这些天内置在 CPU 中)由于某些非法操作而产生浮点中断,但中断并没有在尝试下一个浮点运算之前会被注意到。所以这个崩溃可能是上一个浮点运算的结果,它可能在任何地方。" - TonyK

这确实是问题所在:在我使用 IsSame 进行的比较中,另一个值是 NaN(在这种情况下这是一个有效值),尽管它很高兴地从 swap_float 中减去它,但它设置了一个标志来表示报告下一个操作为错误。我不得不说我完全没有意识到这是可能的——我认为如果它有效,它就会有效。

【讨论】:

    【解决方案2】:

    个人条目可能不会被填充 - 但尽我所能 理解,这只是意味着 swap_float 将被设置为 随机浮动?

    绝对不是。 IEEE 浮点数中的某些位模式表示无效的数字——例如,溢出算术运算的结果,或无效的结果(例如 0.0/0.0)。这里令人费解的是,调试器显然接受该数字为有效,而cout 不接受。

    尝试获取swap_float 的位布局。在 32 位系统上:

    int i = *(int*)&swap_float;
    

    然后以十六进制打印i,让我们知道你看到了什么。

    更新为添加:来自 Mike 的评论,i=1238430338,即 49D0F282 十六进制。这是一个有效的浮点数,正好等于 1711696.25。所以我不知道发生了什么事,我很害怕。我唯一可以建议的是,编译器可能正在将无效的浮点数直接从mrSwap 数组加载到浮点寄存器组中,而不通过swapFloat。因此,swapFloat 的真实值对调试器根本不可用。要检查这一点,请尝试

    int j = *(int*)(mrSwap+30-1);
    

    告诉我们你看到了什么。

    再次更新添加:另一种可能性是延迟浮点陷阱。浮点协处理器(这些天内置在 CPU 中)由于某些非法操作而产生浮点中断,但直到 下一个 浮点操作完成后才会注意到该中断尝试过。所以这个崩溃可能是 previous 浮点运算的结果,它可能在任何地方。祝你好运……

    【讨论】:

    • 谢谢托尼 - 值得了解花车。当我按照您的建议进行操作时,调试器中的 i=1238430338 以及发送到 cerr 时。我会用十六进制打印它...
    • 好的,十六进制,i=49d0f282。我也尝试过使用 printf("%f", swap_float),但结果完全相同。
    • 第一次编辑后,j=49d0f282 - 所以和以前一样。在第二次编辑中:这听起来令人不快,因为调试器似乎最终处于“混乱”状态 - 堆栈的第二个条目是:“[下面的帧可能不正确和/或丢失,没有为 ntdll.dll 加载符号]”。不幸的是,我没有使用调试器的经验,所以我可能没有得到所有的线索......
    • 不过,我知道之前的浮点运算是什么。我的 sn-p 中的最后一组 '...' 实际上是对这个函数的调用: bool IsSame(float swap_value, double function_value, double tolerance) { return (abs(swap_value - (float)function_value)
    • 明白了!你是绝对正确的,TonyK - 在我使用 IsSame 的比较中,另一个值是 NaN(在这种情况下这是一个有效值),虽然它很高兴地从 swap_float 中减去它,但它设置了一个标志来表示报告 next 操作为错误。我不得不说我完全没有意识到这是可能的——我认为如果它有效,它就会有效。
    猜你喜欢
    • 1970-01-01
    • 2017-12-10
    • 2016-01-03
    • 2014-11-23
    • 1970-01-01
    • 1970-01-01
    • 2021-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多