我对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相同的值。
我的期望是否过高,或者微软是否未能遵守某些标准?
答案 0 :(得分:5)
虽然问题没有明确说明这一点,但我认为提问者预期的输出是:
g=1.4822e-323 e=1.4822e-323
这是我们对C / C ++编译器的期望,它承诺严格遵守IEEE-754。问题已标记为C
和C++
,我将在此处说明C99
,因为这是我手头的标准。
在附件F中,描述了IEC 60559浮点运算(其中IEC 60559基本上是IEEE-754的另一个名称),C99
标准规定:
定义
__STDC_IEC_559__
的实现应符合 本附件中的规范。 [...]scalbn
和scalbln
<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