【问题标题】:Does float in Linux gcc (5.4.0) obey IEEE754 rules?Linux gcc (5.4.0) 中的 float 是否遵守 IEEE754 规则?
【发布时间】:2018-01-11 03:34:01
【问题描述】:

我的编程环境是 gcc 版本 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)

我的代码如下:

#include <stdio.h>

typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, int len){
  int i;
  for (i = 0; i<len; i++)
    printf(" %.2x", start[i]);
  printf("\n");
}

void show_float(float x){
  show_bytes((byte_pointer)&x, sizeof(float));
}

int main(){
  int y = 0xffffff;
  float f = y;
  show_float(f);
  return 0;
}

机器给出结果:00 00 00 e0

我认为根据 IEEE 754 是不对的;但我不知道为什么。 而 Windows 中 VS 2013 中的相同代码给出了正确答案:ff ff 7f 4b

gcc 5.4.0 不采用 IEEE 754 吗? 还是我的代码有问题?

【问题讨论】:

  • 不要typedef 指点迷津;那就是疯狂....
  • 此代码使用 gcc 4.9.4 为我输出 ff ff 7f 4b
  • 我在 Windows 上使用 mingw-64 7.1.0 得到了相同的输出 (ff ff 7f 4b)。如果您确实运行了完全相同的代码,那么它似乎必须使用不同的表示。我建议您尝试一些其他数字,看看它们的比较情况
  • 你的机器是小端还是大端? (你可以用宏检查#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
  • 刚刚使用 gcc 5.4 将您的代码粘贴到 godbolt.org/g/cZGBuA。在 main 的组装中,它清楚地用 -O2 加载了 0x4b7fffff。使用 -O1 时,常数会在某处发出并被引用。如果编码不同,硬件指令将不起作用,假设这些指令与 IEEE754 一起工作

标签: c linux gcc floating-point ieee-754


【解决方案1】:

gcc 5.4.0 不采用 IEEE 754 吗?
还是我的代码有问题?

gcc 5.4.0 和 IEEE 754 不是问题。当然代码不符合要求


通过重新排序函数,但代码相同,我收到 2 个警告,并且可以复制 OP 的输出 00 00 00 e0

警告:函数“show_float”的隐式声明 [-Wimplicit-function-declaration]
警告:“show_float”的类型冲突

我怀疑 OP 没有发布真正的代码。 - 或者不是全部在一个文件中。实际代码通常存在代码传递double 的问题——由于缺少事先声明/定义,但show_float() 期望float

#include <stdio.h>

typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, int len){
  int i;
  for (i = 0; i<len; i++)
    printf(" %.2x", start[i]);
  printf("\n");
}

int main(){
  int y = 0xffffff;
  float f = y;
  show_float(f);  // code lacks proto-type, so assumes it needs to pass a double
  return 0;
}

void show_float(float x){
  show_bytes((byte_pointer)&x, sizeof(float));
}

通过声明原型或重新排序代码来修复。

#include <stdio.h>

typedef unsigned char *byte_pointer;
void show_bytes(byte_pointer start, int len);
void show_float(float x);

/* the 3 functions in any order */

【讨论】:

  • 你是对的!我没有将所有代码放在一个文件中。我把main函数放在main.c中,把typedef子句、show_bytes函数和show_float函数都放在show_bytes.c中。但是,我使用命令行 'gcc main.c show_bytes.c -o main' 和 'gcc show_bytes.c main.c -o main' 并运行 ./main
  • @HannanKan 这是 .h 文件的值之一。将typedef unsigned char *byte_pointer; void show_bytes(byte_pointer start, int len); void show_float(float x); 放入 .h 文件中,并将其包含在每个 .c 文件中。
  • 你是对的!我没有将所有代码放在一个文件中。我把main函数放在main.c中,把typedef子句、show_bytes函数和show_float函数都放在show_bytes.c中。但是,我使用命令行 'gcc main.c show_bytes.c -o main' 和 'gcc show_bytes.c main.c -o main' 并运行 ./main 。结果是一样的——00 00 00 e0。从理论上讲,我将它们编译在一起,因此 main 中使用的函数应该找到它的定义位置并正确调用。事实表明,函数调用是可以进行的,但它得到了错误的答案。我不知道出了什么问题。
  • @HannanKan "理论上,我将它们编译在一起,因此 main 中使用的函数应该找到它的定义位置并正确调用" --> 不,使用建议的原型。
  • @chux:再次展示人们发布他们的真实代码是多么重要,而不是看起来很像的东西。问题是由于缺少原型造成的。这在发布的代码中看不到。伟大的侦探工作!
【解决方案2】:

gcc 不使用不同的单浮点表示,而是在程序中的某处将其提升为双精度。转换可能发生在对show_bytes 的调用中,但也可能在其他地方。如果您将浮点数更改为双精度数,您可能会再次获得相同的错误输出,它可能会同时使用两种编译器。

这可能是一个错误或 gcc 的优化,但对于 c 编译器来说这不是正常行为,请参阅this question

#include <stdio.h>

typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, int len) {
    int i;
    for (i = 0; i<len; i++)
        printf(" %.2x", start[i]);
    printf("\n");
}

void show_float(double x) {
    show_bytes((byte_pointer)&x, sizeof(float));
}

int main() {
    int y = 0xffffff;
    double f = y;
    show_float(f);
    return 0;
}

这个输出

00 00 00 e0

如果您将程序中的sizeof(float) 更改为sizeof(double) 输出不正确,您将能够看到整个双精度来验证这一点

对我来说,我使用双打

00 00 00 e0 ff ff 6f 41

【讨论】:

  • 有趣的解释,但如果在原始程序中有一个隐式提升为 double,那就是编译器错误。据我所见,程序应按预期转储float 中的四个字节。
  • @FlorianWeimer 我同意,我也在考虑错误,尽管我怀疑 c 标准禁止这种行为。直到 OP 尝试代码才能确定。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-19
  • 2016-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多