应正确舍入

时间:2015-08-21 23:56:41

标签: c++ c visual-studio floating-point

我对MSVC ldexp行为感到有些惊讶(它发生在Visual Studio 2013中,但也适用于所有旧版本,至少到2003年......)。

例如:

#include <math.h>
#include <stdio.h>
int main()
{
    double g=ldexp(2.75,-1074);
    double e=ldexp(3.0,-1074);

    printf("g=%g e=%g \n",g,e);
    return 0;
}

打印

g=9.88131e-324 e=1.4822e-323

第一个g是奇怪的圆形...
它是2.75 * fmin_denormalized,所以我绝对期望第二个结果e。
如果我评估2.75*ldexp(1.0,-1074)我正确得到与e相同的值。

我的期望是否过高,或者微软是否未能遵守某些标准?

3 个答案:

答案 0 :(得分:5)

虽然问题没有明确说明这一点,但我认为提问者预期的输出是:

g=1.4822e-323 e=1.4822e-323

这是我们对C / C ++编译器的期望,它承诺严格遵守IEEE-754。问题已标记为CC++,我将在此处说明C99,因为这是我手头的标准。

在附件F中,描述了IEC 60559浮点运算(其中IEC 60559基本上是IEEE-754的另一个名称),C99标准规定:

  

定义__STDC_IEC_559__的实现应符合   本附件中的规范。 [...] scalbnscalbln   <math.h>中的函数提供了推荐的scalb函数   IEC 60559的附录。

在该附件的后面,F.9.3.6节规定:

  

在二进制系统上,ldexp(x, exp)相当于scalbn(x, exp)

C99标准引用的附录是1985年版IEEE-754的附录,其中我们发现scalb函数定义如下:

  

Scalb(y,N)返回y×2 N 表示积分值N而不计算2 N

scalb被定义为乘以2的幂,并且必须根据标准根据当前的舍入模式正确舍入乘法。因此,符合C99编译器ldexp()必须返回正确舍入的结果如果,则编译器定义__STDC_IEC_559__。如果没有库调用设置舍入模式,则默认舍入模式“舍入到最近或甚至”有效。

我无法访问MSVC 2013,因此我不知道它是否定义了该符号。这甚至可能取决于编译器标志设置,例如/fp:strict

在追踪我的C ++ 11标准副本后,我找不到任何关于__STDC_IEC_559__的引用或任何有关IEEE-754绑定的语言。根据{{​​3}}的答案,这是因为参考C99标准包含了这种支持。

答案 1 :(得分:1)

这是因为在ldexp计算期间2.75被截断为2,这是因为在那个小的非规范化数字处,代表'.75'部分的位从可表示数字的末尾移开并消失。无论是错误还是设计行为都可以辩论。

计算2.75*ldexp(1.0,-1074)正常舍入时,2.75变为3。

编辑:ldexp应正确舍入,这是一个错误。

答案 2 :(得分:0)

OP结果不符合C规范,因为该规范没有定义计算的准确性。

OP结果可能已失败IEEE 754,但它取决于当时使用的舍入模式,该模式未发布。然而,OP的报告2.75*ldexp(1.0,-1074)按预期工作,这意味着预期的舍入模式已经生效。

使用printf("%la",x)帮助清楚地了解double限制附近发生的情况。

我希望g使用0x1.8p-1073的结果“舍入到最近 - 与偶数相关” - 这与Windows上的gcc一起发生。

理想情况下,g的值为0x1.6p-1073

0x0.0p-1073 Zero
0x0.8p-1073 next higher double DBL_TRUE_MIN
0x1.0p-1073 next higher double
0x1.6p-1073 ideal `g` answer, but not available as a double
0x1.8p-1073 next higher double

公平地说,它可能是一个处理器错误 - 它有happened before

参考

double g=ldexp(2.75,-1074);
printf("%la\n%la\n", 2.75,ldexp(2.75,-1074));
printf("%la\n%la\n", 3.0 ,ldexp(3.0 ,-1074));
double e=ldexp(3.0,-1074);
printf("%la\n%la\n", g,e);
printf("%la\n%la\n", 9.88131e-324, DBL_TRUE_MIN);
printf("g=%g e=%g \n",g,e);

0x1.6p+1
0x1.8p-1073
0x1.8p+1
0x1.8p-1073
0x1.8p-1073
0x1.8p-1073
0x1p-1073
0x1p-1074
g=1.4822e-323 e=1.4822e-323