在尝试在C ++程序中使用libdfp时,我看到了一些奇怪的行为。具体来说,似乎GCC总是四舍五入到小数点后8位,即使我使用的是64位和128位十进制类型。
为了测试这个,我创建了一个非常简单的测试程序:
std::decimal::decimal64 testval = 0.044575289999999997DD;
printf("Decimal float test: expected=0.044575289999999997, actual=%.16Da\n", testval);
哪个输出:
Decimal float test: expected=0.044575289999999997, actual=0.04457529000000000
我很确定这不是libdfp中的打印问题,因为我能够跟踪源并发现该数字已经被printf处理程序的第一行舍入。另外,printf处理程序也会循环,但我已经验证这个代码没有被调用。
作为参考,我正在构建libdfp:
./configure --with-backend=libdecnumber --enable-decimal-float=bid && make
我怀疑问题要么是基础的十进制浮动表示(在我的情况下是BID),要么是由GCC提供的原始类型。它几乎看起来好像所有内容都被舍入到32位十进制浮点数的大小。我的主机拱是x86_64所以这应该本身都支持。此外,GCC确实具有相应的_Decimal[32|64|128]
类型,并且可以在系统上找到<decimal/decimal>
。我正在使用Fedora 25构建本机x86_64 CPU(Intel Xenon)。 AFAIK,这个处理器没有本机十进制浮点支持,所以一切都在软件中呈现。
我唯一的线索是GCC没有在配置摘要中列出--enable-decimal-float
构建选项:
$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/6.3.1/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --disable-libgcj --with-isl --enable-libmpx --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 6.3.1 20161221 (Red Hat 6.3.1-1) (GCC)
$ g++ --version
g++ (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1)
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
话虽这么说,我的编译器 定义_Decimal[32|64|128]
类型并提供运算符&lt;&lt;他们的重载。如果没有启用此类支持,我根本不会期望这些内容可用。我当然不应该使用它们来编译程序,并且几乎有效输出。
最后,我可以看到这是libdecnumber存在的一个问题,但我目前只知道谁管理这些类型的分配。
以前有人见过这个问题吗?如果失败了,有没有人在类似的设置上构建并成功使用libdfp?哪一个软件控制内部表示的四舍五入(而不是四舍五入显示)?
修改
我终于设法进行了反汇编。似乎正在将完整值加载到rdx
并调用decimal64
构造函数。值0x2fafd619589efa00
以十进制为3436200445056317952
,我怀疑是0.044575289999999997
以BID格式表示。我不确定GCC如何知道使用BID vs DPD,因为这是在libfdp构建时指定的,但是我将为另一篇文章留下那个特别的谜。
如果我的理解是正确的,那似乎意味着海湾合作委员会正在进行四舍五入。除了重建编译器(我真的想避免)之外,还有什么可以做的吗?我知道IEEE-754提供了调整机制。 fp操作的行为(包括舍入模式),GCC是否会向用户公开这些操作?
拆卸
│0x401180 <main(int, char**)> push rbp │
│0x401181 <main(int, char**)+1> mov rbp,rsp │
│0x401184 <main(int, char**)+4> sub rsp,0x30 │
│0x401188 <main(int, char**)+8> mov DWORD PTR [rbp-0x14],edi │
│0x40118b <main(int, char**)+11> mov QWORD PTR [rbp-0x20],rsi │
b+ │0x40118f <main(int, char**)+15> movabs rdx,0x2fafd619589efa00 │
│0x401199 <main(int, char**)+25> lea rax,[rbp-0x10] │
│0x40119d <main(int, char**)+29> mov QWORD PTR [rbp-0x28],rdx │
│0x4011a1 <main(int, char**)+33> movq xmm0,QWORD PTR [rbp-0x28] │
│0x4011a6 <main(int, char**)+38> mov rdi,rax │
│0x4011a9 <main(int, char**)+41> call 0x4012e6 <std::decimal::decimal64::decimal64(decimal64)> │
│0x4011ae <main(int, char**)+46> mov rax,QWORD PTR [rbp-0x10] │
│0x4011b2 <main(int, char**)+50> mov QWORD PTR [rbp-0x28],rax │
│0x4011b6 <main(int, char**)+54> movq xmm0,QWORD PTR [rbp-0x28] │
│0x4011bb <main(int, char**)+59> mov edi,0x4018e8 │
│0x4011c0 <main(int, char**)+64> mov eax,0x1 │
│0x4011c5 <main(int, char**)+69> call 0x400a30 <printf@plt> │
│0x4011ca <main(int, char**)+74> mov eax,0x0 │
│0x4011cf <main(int, char**)+79> leave │
│0x4011d0 <main(int, char**)+80> ret │
│0x4011d1 <__static_initialization_and_destruction_0(int, int)> push rbp │
│0x4011d2 <__static_initialization_and_destruction_0(int, int)+1> mov rbp,rsp │
│0x4011d5 <__static_initialization_and_destruction_0(int, int)+4>────────sub rsp,0x10───────────────────────────────────────────────────────────│
│0x4011d9 <__static_initialization_and_destruction_0(int, int)+8> mov DWORD PTR [rbp-0x4],edi │
│0x4011dc <__static_initialization_and_destruction_0(int, int)+11> mov DWORD PTR [rbp-0x8],esi │
│0x4011df <__static_initialization_and_destruction_0(int, int)+14> cmp DWORD PTR [rbp-0x4],0x1 │
│0x4011e3 <__static_initialization_and_destruction_0(int, int)+18> jne 0x40120c <__static_initialization_and_destruction_0(int, int)+59> │
│0x4011e5 <__static_initialization_and_destruction_0(int, int)+20> cmp DWORD PTR [rbp-0x8],0xffff │
│0x4011ec <__static_initialization_and_destruction_0(int, int)+27> jne 0x40120c <__static_initialization_and_destruction_0(int, int)+59> │
│0x4011e5 <__static_initialization_and_destruction_0(int, int)+20> cmp DWORD PTR [rbp-0x8],0xffff │
│0x4011ec <__static_initialization_and_destruction_0(int, int)+27> jne 0x40120c <__static_initialization_and_destruction_0(int, int)+59> │
│0x4011ee <__static_initialization_and_destruction_0(int, int)+29> mov edi,0x60309d │
│0x4011f3 <__static_initialization_and_destruction_0(int, int)+34> call 0x400ae0 <_ZNSt8ios_base4InitC1Ev@plt> │
│0x4011f8 <__static_initialization_and_destruction_0(int, int)+39> mov edx,0x4018d8 │
│0x4011fd <__static_initialization_and_destruction_0(int, int)+44> mov esi,0x60309d │
│0x401202 <__static_initialization_and_destruction_0(int, int)+49> mov edi,0x400aa0 │
│0x401207 <__static_initialization_and_destruction_0(int, int)+54> call 0x400ac0 <__cxa_atexit@plt> │
完成测试源
#include <float.h>
#include <decimal/decimal>
#include <math.h>
#include <fenv.h>
#include <stdlib.h>
#include <wchar.h>
#include <cstdlib>
int main (int argc, char *argv[])
{
std::decimal::decimal64 testval = 0.044575289999999997DD;
printf("Decimal float test: expected=0.044575289999999997, actual=%.16Da\n", testval);
return EXIT_SUCCESS;
}