18

为什么这是真的?与 C 甚至 Java Math.pow 方法相比,当将两个浮点数相乘时,Java 产生的结果似乎略有差异。

爪哇:

float a = 0.88276923;

double b = a * a;   // b becomes 0.779281497001648  <---- what???
b = Math.pow(a,2);  // b becomes 0.7792815081874238

C:

float a = 0.88276923;

double b = a * a;   // b becomes 0.7792815081874238
pow(a,2);           // b becomes 0.7792815081874238

更新:根据 Ed S. 的评论,我还发现 C 行为会根据编译器而变化。使用 gcc 它似乎与 Java 行为相匹配。使用 Visual Studio(取决于您的目标平台)它可以产生上面看到的结果或在 Java 中看到的结果。啊。

4

2 回答 2

16

正如 pst 和真实性已经明智地指出的那样,C在乘法之前float将 a 提升为 a 。实际上,当它们被压入堆栈时,它们被提升为 80 位扩展精度值。这是汇编程序输出(VS2005 x86 C89)double

    double b = a * a;
00411397  fld         dword ptr [a] 
0041139A  fmul        dword ptr [a] 
0041139D  fstp        qword ptr [b] 

FLD 指令

FLD 指令将 32 位、64 位或 80 位浮点值加载到堆栈中。该指令在将值压入浮点堆栈之前将 32 位和 64 位操作数转换为 80 位扩展精度值。


有趣的是,如果我以 x64 为目标进行构建,movss则会使用该指令并且您会得到一个值0.779281497001648作为结果,即您在 java 示例中看到的内容。试试看。

于 2012-04-17T23:50:07.780 回答
10

Java 的用途

double b = a * a;

首先乘以a * a(32 位) ,然后在分配给 时将float结果转换为(64 位)。doubleb

b = Math.pow(a,2);

首先转换a为(64 位)double(因为参数Math.powdouble, double),然后平方。

(对我来说)令人费解的是为什么 C 似乎将a'sdouble放在首位

double b = a * a;

这在标准中吗?

编辑:我隐约记得C不需要数字的特定实现(就使用了多少位而言)......这是怎么回事?你的floats 是 64 位的吗?(在 Java 中 afloat始终为 32 位,adouble始终为 64 位)。

编辑: Ed S. 的回答和标记的评论都表明不同的编译器给出不同的结果表明 C 结果是特定于实现和体系结构的。

于 2012-04-17T23:41:26.140 回答