注册 登录
编程论坛 C语言论坛

以下两份代码为何有这差别?

hffjhhh 发布于 2020-06-11 23:12, 2902 次点击
第一份代码的输出结果为3.300000,代码如下:
程序代码:
#include"stdio.h"
int main(void){
    double num;
    num=3.3;
    printf("%lf",num);
    return 0;
}

第二份代码的输出结果为0.000000,代码如下:
程序代码:
#include"stdio.h"
int main(void){
    long double num;
    num=3.3;
    printf("%lf",num);
    return 0;
}

为何第二份代码加了个long,就产生这样的输出结果?
15 回复
#2
ditg2020-06-12 02:30
程序代码:
#include <stdio.h>

int main(int argc, char **argv)
{
    printf("%lf\n", 0x400a660000000000);
}


有个截断问题,手工只能算到这种程度了(给的3.3实在有点缺德)……

注:突然发现个重大问题,程序在龙芯3A2000下可以得到正确结果,但在intel i56400 gcc-6.3.0 deepin-15.11平台上结果全是0

[此贴子已经被作者于2020-6-12 08:24编辑过]

#3
rjsp2020-06-12 07:19
long double 用 "%Lf",大写的L
#4
lin51616782020-06-12 09:21
格式和实参类型不匹配是未定义行为
这是出现这个情况的标准依据
再具体实现中
printf 根据格式化字符判断实参类型 进而决定读取多少个字符进行类型解析
这里假如 double 类型8字节 long double 类型12字节
那么 实参入栈long double 至少12个字节 printf调用到时候 按照%f处理只读取8个字节
GG

而两个类型的大小在不同编译环境中是不确定的
有的编译环境 long double 和 double 一样大
所以你有时候能看到 输出和double一样的结果
这不奇怪 这只是作死
#5
ditg2020-06-12 13:05

1. 已实验完成long(包括long long)、double类型;long double还没整出来。起码可以肯定的是,gcc优先用寄存器传递参数(有时间再慢慢看是传递的地址还是内容),寄存器用完后,才会选择栈(估计windows也差不多)。

2. 如果前述double编码正确,那么long double类型的编码形式应该可以倒推出来;因为可以先用sizeof(long double)知道编码字节数,比如12或16字节(如果写书,用未定义应该也算正确)。
#6
hffjhhh2020-06-12 13:19
以下是引用rjsp在2020-6-12 07:19:19的发言:

long double 用 "%Lf",大写的L

即使改成了Lf也一样还是打印0.000000.
程序代码:
#include"stdio.h"
int main(void){
    long double num;
    num=3.3;
    printf("%Lf",num);
    return 0;
}
#7
ditg2020-06-12 14:02
%llf再试一下
#8
hffjhhh2020-06-12 14:37
以下是引用ditg在2020-6-12 14:02:39的发言:

%llf再试一下

试了,还是不行。还是打印0.000000
#9
ditg2020-06-12 18:40
http://www.
(网站已测试,关于数据转换的)

也不知是intel还是gcc默认不是IEEE 754浮点编码标准;long double使用浮点运算单元。


(以后谁要再拿long double类型说事,小心我拿烟头烫他!)

[此贴子已经被作者于2020-6-12 18:56编辑过]

#10
ditg2020-06-12 19:15
回复 8楼 hffjhhh
如果是我会像下面这样:

先sizeof(long double),看是否支持某种类型;如果=16(其它值暂时没见过);再执行汇编浮点指令finit或fninit,接着随便执行一条你确实知道返回值含义的浮点指令,判断有没有协处理器(我试了一下,用户空间好像也能执行);正常后试着看一下程序的返回值。如果一切正常,我认为就是编译器后端解码的问题
#11
ditg2020-06-13 12:51
只有本站会员才能查看附件,请 登录

只有本站会员才能查看附件,请 登录


龙芯3A2000手册提供的依据和编码格式;有理由认为intel不是IEEE 754标准(网上的很多文章估计过时了)
#12
hffjhhh2020-06-13 16:00
回复 11楼 ditg
我没有学过汇编,因此不知道如何用汇编指令来验证是否是intel处理器的问题。
本机上sizeof(long double)的大小为16个字节。
如果intel处理器不是IEEE 754标准,那么是否意味着就是处理器的问题导致无法正确计算16个字节的long double类型的数据呢?
如果不是处理器的问题,那么是否意味着就是编译器的问题导致无法计算16个字节的long double类型的数据呢?也无法用%llf格式来打印16个字节的long double类型的数据呢?
#13
fulltimelink2020-06-13 17:30
%Le
#14
fulltimelink2020-06-13 17:38
搬运工
The problem is that the compiler (GCC) and the runtime library (Microsoft's) are implemented by different groups that happen to have different ideas about how the type long double should be represented. (gcc uses 128 bits for long double; Microsoft uses 64 bits, with the same representation as double.)

Either choice of representation is perfectly legitimate, but they're incompatible with each other. It's not a bug either in GCC or in Microsoft's library, but in the way MinGW integrates them.

Your options are to use an implementation other than MinGW, to write or copy code that handles long double correctly , or to avoid calling any library functions that take arguments or return results of type long double (computations on long double shouldn't be a problem as long as they don't call any library functions). For example, you can convert to double and print with %g, with some loss of range and precision.

Another (probably better) workaround is to compile with -D__USE_MINGW_ANSI_STDIO, which causes MinGW to use its own implementation of printf and friends rather than relying on the Microsoft implementation.

#15
ditg2020-06-13 17:54
long double我还没完全整出来(宿舍没有intel电脑,就算有也没有linux系统的),前面说的是double类型(64位)。

我认为影响编译结果的只有cpu指令和编译器。两个假设:假设相同编译器在不同平台间遵守相同的设计规则;那么输出结果也应该相同。假设前面给的网站编码结果正确(反正我计算得到这个编码以及龙芯手册给的的确是真实结构),那么下面代码应该输出3.3。目前相同代码在龙芯上输出3.3(龙芯说它依照的是IEEE 754),intel输出0.0。反正intel的输出结果总有问题吧?!(欢迎拍砖)

程序代码:
#include <stdio.h>

int main(int argc, char **argv)
{
    printf("%lf\n", 0x400a666666666666);
}


结果:
只有本站会员才能查看附件,请 登录


(刚看到,上面的引用不太相关,因为实验没有使用windows系统)

[此贴子已经被作者于2020-6-13 18:02编辑过]

#16
rjsp2020-06-15 09:05
以下是引用hffjhhh在2020-6-12 13:19:50的发言:
即使改成了Lf也一样还是打印0.000000.


如果你用的是 MinGW,那么应该如14楼 fulltimelink 说的那样做,或在 #include <stdio.h> 之前加上 #define __USE_MINGW_ANSI_STDIO 1
如果不是 MinGW 的话,该升级一下编译器了,因为C标准在上个世纪就支持long double了。
1