一个浮点数跨平台产生的问题 – AlanTu

一个浮点数跨平台产生的问题 – AlanTu

致谢使声尖锐(微博@使声尖锐姓)的奉献,原文在使声尖锐的视频博客上(原文地址),原文辨析不敷好,能够会给不对的劝告家属,因而,我对原文做了很多改动。,并添加Linux的心甘情愿的。浮点法数是每一特若干复杂的成绩。,祝福本文能帮忙您默许FLULA的编辑选择。。(注:我缺乏窗户 32位与C说使处于某种特定的环境之下,因而,倾向于Windows 32位顺序和C说顺序缺乏成为校对。

放简略地说,最新的突出,C,写,触及浮点法运算,往返挽回,直截了当地看下面的编码。

1

2

3

4

floatp3x =

floatp2y =

doublev321 = p3x * p2y;

(v321);

很简略吧,出狱-202014162。,没成绩,难道C不会的发生异样的卒吗?这是不能相信的的,吐艳视觉 Studio,尝试付印编码,卒,出狱-202014162。。一切的都完毕了吗?显然指责!你把编辑时的选择从AnyCPU改成x64试试~(保养使处于某种特定的环境之下几乎64位滴哦!!出狱-202014160,这是得体的的。,它是-202014160。相当多的不相信,再跑两遍,它依然是- 202014160。呃,想通了,鉴于浮点法运算的离经叛道的行为,202014160出狱有理的。。

为什么有理?这是定期地的。,因下面的p3x和py是两个浮点法典型,还V321是双重的,但它异样两个浮点法型,在应验后替换为两倍。,浮标准确的仅为7。,因而,数亿人,自由自在无法公约精密

还为什么修正典型的CPU有辨别的后果呢?嗯,让我们家再来一次C/C。

1

2

3

4

5

6

7

8

9

10

11

12

13

#include

usingnamespacestd;

intmain()

{

    floatp3x =

    floatp2y =

    doublev321 = p3x * p2y;

    STD:(15)

    炽烈的:咳嗽 << v321 << std::endl;

    return0;

}

在辨别的平台下面的C 编码的卒列举如下。:

  • Windows 32/64位下:-202014160
  • Linux 64位(CITOS) 6 gcc 4.4.7)-202014160,
  • Linux 32位(Ubuntu 12.04+ gcc 4.6.3)是:-202014162

有理的卒应该是-202014160。,手术的得体的出狱-202014162。,有理性是鉴于浮点法准确的不可形成的。。也许乘以两倍,就能成为得体的有理的卒。:申报p3x和P2Y型以上所述的C 顺序为双。,你可以成为得体的的卒,因双准确的是双准确的,浮标是单准确的的,因而double有十足的位数保证金更多的数位)。但我们家某个也不是懂。,为什么Linux 32位,可以锻炼得体的的美国昆腾公司,而指责每一有理的数字

与C++同上,C是32和64(在调试下),在下面所说的事结束缺乏分歧的卒。,那我们家看一下C++/C#的缀编编码(使应用GDB的反缀编 /m main 命令,以及,以下仅显示 float * float 与将其替换为双编码的缀编顺序

Linux平台下的G 编辑

1

2

3

4

5

6

7

//C++ 32位制下 Ubuntu 12.04

8       doublev321 = p3x * p2y;

   0x0804860f <+27>:  flds   0x18(%esp)

   0x08048613 <+31>:  fmuls  0x1c(%esp)

   0x08048617 <+35>:  fstpl  0x10(%esp)

.......

1

2

3

4

5

6

7

//C++ 64位制下 CentOS 6

9           doublev321 = p3x * p2y;

   0x000000000040083c <+24>:    movss  -0x20(%rbp),%xmm0

   0x0000000000400841 <+29>:    mulss  -0x1c(%rbp),%xmm0

   0x0000000000400846 <+34>:    unpcklps %xmm0,%xmm0

   0x0000000000400849 <+37>:    cvtps2pd %xmm0,%xmm0

   0x000000000040084c <+40>:    movsd  %xmm0,-0x18(%rbp)

Windows平台下的想像 Studio编辑

1

2

3

4

5

//C# AnyCPU编辑,Windows VS2012

doublev321 = p3x * p2y;

00000049  fld         dword ptr [ebp-40h]

0000004c  fmul        dword ptr [ebp-44h]

0000004f  fstp        qword ptr [ebp-4Ch]

1

2

3

4

5

6

//C# X64位编辑 Windows7 VS2012

doublev321 = p3x * p2y;

009B43B8 movss xmm0,dword ptr [p3x]

009B43BD mulss xmm0,dword ptr [p2y]

009B43C2 cvtss2sd xmm0,xmm0

009B43C6 movsd mmword ptr [v321],xmm0

从下面的缀编编码可以看出,无论是Linux温柔的Windows,C 或C 32和64与漂POI的缀编使听写不相同的。。 产生物质的反应编码的32位编码是FLD/FMUL/FSTP等。,64位使听写应用MOVSS/Muls/MOVSD。看下降,这如同与平台相干到。。

我们家持续考察,我们家被发现的人,FLD/FMUL/FSTP和如此等等使听写源自FPU(浮标 point 单元)浮点法运算加工,正确的说,它是FPU x87使听写,FPU在举行打中浮点法运算,用了80位表达的互插浮点法运算,与,鉴于浮点法/双截获32或64位。,默许环境下,FPU将最小的由FO需求账目的准确的成绩。。可理解浮点法运算基准IEEE-754 使整洁基准完成者做准备浮点法可涂准确的体式(Extended 准确的),Intel x86加工有FPU(浮标 point 单元)浮点法计算加工背衬下面所说的事涂。

非FPU的环境是用了SSE中128位表达(浮标真正只用了其打中32位,计算也以32位计算。,这是成绩的根本账目。。原文末了的挑剔的辨析。

我发生。,我们家可以使守规矩 g++ 看文献,我们家可以找到每一编辑选择:-mfpmath,在32位,此编辑选择的默许值为:387,那就是X87 FPU使听写,64位下,此编辑选择的值为,换句话说,应用SSE的使听写。因而,倾向于本文打中下面所说的事例,也许在64位下添加像-MFP算学=387,你会成为得体的的卒,而指责有理的卒。

在VS2012,C,可以设置编辑选择(在编码产生物质的反应中)可选,FP: | fast | 缜密的的],在这种环境下,使分娩 32位用precise 或许 缜密的将成为有理的卒(- 202014160),敏捷的会发生得体的的卒(- 202014162), fast debug/release下卒也不是同上哦(release下才最优化了)。64体系下多方面的卒可以每个人本身去受测验下(Debug/Release),让我们家看一眼VS编辑后产生物质的反应的中央编码。。(陈皓注:我的VS2012是在调试中编辑的,轻蔑的拒绝或不承认您到何种地步设置/FP的决定因素值,缀编顺序是同上的。,应用SSE使听写,使分娩是辨别的,还我的发行编码的编辑是特若干奇特的事物的,源编码是,历年缺乏发达窗户。,VS的应用仅在VC6 /VC2005上

因而,我们家源自X87 FPU使听写向SSE使听写做编码经得起移植的时分,我们家能够会对决浮点法数准确的的成绩。,在数量庞大的数量庞大的迷信计算中,下面所说的事准确的成绩会更糟。。成绩指责简略地在32和64的体系中。,下面所说的事成绩首要停止说编辑器的完成。。用更较年长者的说,如:C99或Fortran 2003中,龙的绍介 双做可涂双准确的(涂) 双倍),异样可以撤销更多的准确的成绩。。

如今我们家把顺序改生长 double,(注:典型延长 双倍)

1

2

3

4

5

6

7

8

9

10

11

12

13

#include

usingnamespacestd;

intmain()

{

    longdoublep3x =

    longdoublep2y =

    longdoublev321 = p3x * p2y;

    STD:(15)

    炽烈的:咳嗽 << v321 << std::endl;

    return0;

}

应用GDB的反缀编 /m 首要的是,您将领会柄状物的编辑列举如下:

1

2

3

4

5

6

//linux 32位制

8       longdoublev321 = p3x * p2y;

   0x08048633 <+63>:  fldt   0x10(%esp)

   0x08048637 <+67>:  fldt   0x20(%esp)

   0x0804863b <+71>:  fmulp  %st,ST(1)

   0x0804863d <+73>:  fstpt  0x30(%esp)

1

2

3

4

5

6

//linux 64位制

8           longdoublev321 = p3x * p2y;

   0x0000000000400818 <+52>:    fldt   -0x30(%rbp)

   0x000000000040081b <+55>:    fldt   -0x20(%rbp)

   0x000000000040081e <+58>:    fmulp  %st,ST(1)

   0x0000000000400820 <+60>:    fstpt  -0x10(%rbp)

我们家可以领会,32位制和64位制应用了异样的缀编使听写(自然,我缺乏全都是的物质的机具,我只因为在VMware Play虚拟机的受测验,因而下面的例未必一致的所若干地区。,以及,C/C++说和编辑器和平台有特若干大的相干) ,账目是我们家用了许久。 双涂的双准确的数据典型。(注:也许应用双或漂,Linux上,应用X87的32位 FPU 使听写编辑,64位用SSE使听写编辑。

好了,让我们家再次回到C,C的浮点法是基准的背衬。,心爱的的其正式文献也提到了浮点法运算能够会发生比现场恢复典型高的准确的的值(正列举如下面的现场恢复值准确的就超越了float的准确的),并暗示计算图表硬件背衬其中的哪一个能涂浮点法准确的,这么所若干浮点法运算将以这种准确的举行改良。,诸如,x*y/z, x*y的值能够超越双倍生产能力的范畴。,只因为,真正环境可以分为Z和卒可以重行举行。,异样每一词,使用FPU的卒,成为了每一准确的双值。,而指责FPU是无穷大或什么。。

因而,倾向于C#来说,你显然无法找到每一像C/C++同上的使用编辑器选择的来处理下面所说的事成绩的“处理方案”(实则,应用编辑器决定因素是每一伪解。

并且,为了处理下面所说的事成绩,不需求修正编辑器选择。,因下面所说的事成绩昭著不它是FPU或者SSE的成绩,FPU是一种老一套的技术,上证是任一有理的技术,因而,也许你不舒服计算你的浮点法数,那是什么呢?,你需求正确和正确,得体的的处理方案指责编辑决定因素。,还,必需品应用具有高的准确的音节数的更多数据典型。,譬如:double 或长 double

以及,使安定编码时,必需品确保真正柄状物使处于某种特定的环境之下/T。分歧性(包孕OS结构)、编辑选择等。啊(特殊C/C 并且,编辑器上的决定因素能够有很多坑。,有些坑能够会洒上顺序打中成绩。),若非,会呈现没头没脑的成绩。,为了这个目的花了许久才找到答案。;当触及浮点法运算时,不要忘却这能够是;浮漂/双倍混合应用应特殊小心

Reference:

[1] C# Language Specification Floating point types
[2] Are floating-point numbers consistent in C#? Can they be?
[3] The FPU Instruction Set

反对论证

* -2499.0f = 202014160浮点法运算课程的阐明

32位浮点法数字在计算图表打中表现:1位打手势位(s)- 8位是转位字(e)- 23位无效NU。。
32位浮标 = (-1)^s * (1+m) * 2^(e-127), E是真正替换到*2 ^ e的转位。,M是XXXXX的前景(节省1位)

= 1 0011 1011 1100 0110.0= 1.00111011110001100*2^16
无效位m = 0011 1011 1100 0110 0000 000
转位e = 16 + 127 = 143 =  10001111
心爱的表现 80838.0 =  0 [1000 1111] [0011 1011 1100 0110 0000 000]
= 0100 0111 1001 1101 1110 0011 0000 0000
= 47 9d e3 00 真正调试打中内存值 能够是00 e3 9d 47是因调试使处于某种特定的环境之下应用小端表现的ME。:低音节行存储器低地址端,高内存高地址

-2499.0 = -100111000011.0 = -1.001110000110 * 2^11
无效位m = 0011 1000 0110 0000 0000 000
转位e = 11+127=138= 10001010
打手势位S = 1
-2499的心爱的表现 = 1 [10001010] [0011 1000 0110 0000 0000 000]
=1100 0101 0001 1100 0011 0000 0000 0000
=c5 1c 30 00

80838.0 * -2499.0 = ?

第每一是瞄准。 e = 11+16 = 27
转位e = e + 127 = 154 = 10011010
无效位乘法卒 1.1000 0001 0100 1111 1011 1010 01 你可以本身做
真正中单独的23个。,打扰的后头是1000。 0001 0100 1111 1011 1010 01
乘法卒心爱的表现为=1〔10011010〕〔1000〕。 0001 0100 1111 1011 101]
= 1100 1101 0100 0000 1010 0111 1101 1101
= cd 40 a7 dd

卒 =  -1.1000 0001 0100 1111 1011 101 *2^27
=  -11000 0001 0100 1111 1011 1010000
=  -202014160
翻到双或202014160后。

也许它是FPU的话,是你这么说的嘛!无效位卒不会的被打扰。,即
FPU卒 = -1.1000 0001 0100 1111 1011 101001 *2^27
= -11000 0001 0100 1111 1011 1010010
= -202014162

全文完,也许定冠词有不对,我们家迎将得体的。。

发表评论

电子邮件地址不会被公开。 必填项已用*标注