C ++十进制浮点错误舍入

时间:2017-08-14 21:08:32

标签: c++ gcc floating-point

在尝试在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;
}

0 个答案:

没有答案