Ed *_*ynn 3 c double long-double windows-subsystem-for-linux
我想检查 C 中各种浮点类型可以保存的最大整数而不损失精度。这是一个测试程序:
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#define FLOATTYPE long double
#define ONE ((FLOATTYPE)1.0)
#define TWO ((FLOATTYPE)2.0)
int
main(int argc,char*argv[]){
int i;
FLOATTYPE x;
x = ONE;
for(i=0;;++i){
printf("1.0<<%3d: x=%.0Lf",i,(long double)x);
if((x+ONE)!=x &&
(x+ONE)- x == ONE){
printf(" ... can increment without loss of precision\n");
}else{
printf(" ... cannot increment without loss of precision\n");
break;
}
x *= TWO;
}
printf("FLT_RADIX = %d\n",FLT_RADIX);
printf("FLT_MANT_DIG = %d\n",FLT_MANT_DIG);
printf("DBL_MANT_DIG = %d\n",DBL_MANT_DIG);
printf("LDBL_MANT_DIG = %d\n",LDBL_MANT_DIG);
printf("\nsizeof(FLOATTYPE) = %lu\n",sizeof(x));
}
Run Code Online (Sandbox Code Playgroud)
一些结果(使用gcc-9(Ubuntu 9.4.0-1ubuntu1~16.04)9.4.0):
当FLOATTYPEis float:sizeof为 4 时,循环在 处退出i==24,等于FLT_MANT_DIG。
当FLOATTYPEis double:sizeof为 8 时,循环在 处退出i==53,等于DBL_MANT_DIG。
当FLOATTYPEis __float128:sizeof为 16 时,循环在 处退出i==113。
它们都有道理。然而:
FLOATTYPEis long double:sizeof为 16 时,循环在 处退出i==53,它不等于LDBL_MANT_DIG(为 64)。看起来long double占用的内存比 多double,但没有提供更高的精度。怎么会?
编辑:有关编译器等的更多详细信息:这是在 Windows 10 Pro 计算机上,在 Linux 1 的 Window 子系统中托管 Ubuntu 16.04。编译器从以下位置报告此情况gcc-9 -v:
Using built-in specs.
COLLECT_GCC=gcc-9
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 9.4.0-1ubuntu1~16.04' --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --with-system-zlib --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-9-SATzbE/gcc-9-9.4.0/debian/tmp-nvptx/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~16.04)
Run Code Online (Sandbox Code Playgroud)
编译命令很简单:我注释掉了 的定义,FLOATTYPE以便可以编译不同的版本,然后运行:
gcc-9 test_precision0100.c -o test_precision0100_longdouble.exe -DFLOATTYPE="long double"
Run Code Online (Sandbox Code Playgroud)
然后就跑了./test_precision0100_longdouble.exe。除了和-Wall -Wextra -pedantic -std=c99的未使用参数之外,编译器不会给出任何警告消息。argcargv
我使用上面提供的代码中定义的 FLOATTYPE 得到了相同的结果。我使用内置 gcc v5.4.0 也得到了相同的异常结果,但在 WSL2 上托管 Ubuntu 18.04 的另一台机器上却没有得到同样的异常结果。输出看起来与我的描述中所期望的一样,结尾:
1.0<< 50: x=1125899906842624 ... can increment without loss of precision
1.0<< 51: x=2251799813685248 ... can increment without loss of precision
1.0<< 52: x=4503599627370496 ... can increment without loss of precision
1.0<< 53: x=9007199254740992 ... cannot increment without loss of precision
FLT_RADIX = 2
FLT_MANT_DIG = 24
DBL_MANT_DIG = 53
LDBL_MANT_DIG = 64
sizeof(FLOATTYPE) = 16
Run Code Online (Sandbox Code Playgroud)
这是“gcc -S test_ precision0100.c”中的“test_ precision0100.s”,如上所述:
.file "test_precision0100.c"
.text
.section .rodata
.LC1:
.string "1.0<<%3d: x=%.0Lf"
.align 8
.LC2:
.string " ... can increment without loss of precision"
.align 8
.LC3:
.string " ... cannot increment without loss of precision"
.LC4:
.string "FLT_RADIX = %d\n"
.LC5:
.string "FLT_MANT_DIG = %d\n"
.LC6:
.string "DBL_MANT_DIG = %d\n"
.LC7:
.string "LDBL_MANT_DIG = %d\n"
.LC8:
.string "\nsizeof(FLOATTYPE) = %lu\n"
.text
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $48, %rsp
movl %edi, -36(%rbp)
movq %rsi, -48(%rbp)
fld1
fstpt -16(%rbp)
movl $0, -20(%rbp)
.L5:
movl -20(%rbp), %eax
pushq -8(%rbp)
pushq -16(%rbp)
movl %eax, %esi
movl $.LC1, %edi
movl $0, %eax
call printf
addq $16, %rsp
fldt -16(%rbp)
fld1
faddp %st, %st(1)
fldt -16(%rbp)
fucomip %st(1), %st
jp .L9
fldt -16(%rbp)
fucomip %st(1), %st
fstp %st(0)
je .L2
jmp .L7
.L9:
fstp %st(0)
.L7:
fldt -16(%rbp)
fld1
faddp %st, %st(1)
fldt -16(%rbp)
fsubrp %st, %st(1)
fld1
fucomip %st(1), %st
jp .L10
fld1
fucomip %st(1), %st
fstp %st(0)
jne .L2
movl $.LC2, %edi
call puts
fldt -16(%rbp)
fadd %st(0), %st
fstpt -16(%rbp)
addl $1, -20(%rbp)
jmp .L5
.L10:
fstp %st(0)
.L2:
movl $.LC3, %edi
call puts
nop
movl $2, %esi
movl $.LC4, %edi
movl $0, %eax
call printf
movl $24, %esi
movl $.LC5, %edi
movl $0, %eax
call printf
movl $53, %esi
movl $.LC6, %edi
movl $0, %eax
call printf
movl $64, %esi
movl $.LC7, %edi
movl $0, %eax
call printf
movl $16, %esi
movl $.LC8, %edi
movl $0, %eax
call printf
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (Ubuntu 9.4.0-1ubuntu1~16.04) 9.4.0"
.section .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)
这是一个长期存在的 WSL1 错误 - https://github.com/microsoft/WSL/issues/830
您可以尝试通过添加以下内容来解决该问题:
#include <fpu_control.h>
...
int main() {
unsigned short cw = 0x37f;
_FPU_SETCW(cw);
Run Code Online (Sandbox Code Playgroud)
到你的程序。
| 归档时间: |
|
| 查看次数: |
249 次 |
| 最近记录: |