5

这是一个非常简单的问题,但却很重要,因为它极大地影响了我的整个项目。

假设我有以下代码片段:

unsigned int x = 0xffffffff;
float f = (float)((double)x * (double)2.328306436538696e-010); //  x/2^32

我希望它f类似于 0.99999,但取而代之的是四舍五入到 1,因为它是最接近的float近似值。这不好,因为我需要float[0,1) 区间的值,而不是 [0,1]。我确信这很简单,但我会很感激一些帮助。

4

5 回答 5

8

在 C 语言中(自 C99 起),您可以使用fesetroundlibm

#include <stdio.h>
#include <fenv.h>
int main()
{
    #pragma STDC FENV_ACCESS ON
    fesetround(FE_DOWNWARD);
    // volatile -- uncomment for GNU gcc and whoever else doesn't support FENV
    unsigned long x = 0xffffffff;
    float f = (float)((double)x * (double)2.328306436538696e-010); //  x/2^32
    printf("%.50f\n", f);
}

使用 IBM XL、Sun Studio、clang、GNU gcc 进行测试。这0.99999994039535522460937500000000000000000000000000在所有情况下都给了我

于 2013-08-06T16:35:27.803 回答
3

在默认的 IEEE 754 舍入模式下double转换为 1 或更多时的值是(在 C99 的十六进制表示法中,因为您的问题被标记为“C”)。float0x1.ffffffp-1

您的选择是:

  1. 转换前将 FPU 舍入模式转为向下舍入,或
  2. 乘以(0x1.ffffffp-1 / 0xffffffffp0)(给或取一个 ULP)以利用完整的单精度范围 [0, 1) 而不会得到值1.0f

方法 2导致使用常量 0x1.ffffff01fffffp-33

double factor = nextafter(0x1.ffffffp-1 / 0xffffffffp0, 0.0);
unsigned int x = 0xffffffff;
float f = (float)((double)x * factor);
printf("factor:%a\nunrounded:%a\nresult:%a\n", factor, (double)x * factor, f);

印刷:

factor:0x1.ffffff01fffffp-33
unrounded:0x1.fffffefffffffp-1
result:0x1.fffffep-1
于 2013-08-06T16:33:21.010 回答
1

您可以将值截断为最大精度(保留 24 个高位)并除以 2^24 以获得浮点数可以表示的最接近的值,而不会四舍五入为 1;

unsigned int i = 0xffffffff;
float value = (float)(i>>8)/(1<<24);

printf("%.20f\n", value);
printf("%a\n", value);

>>> 0.99999994039535522461
>>> 0x1.fffffep-1
于 2013-08-06T16:50:04.027 回答
1

您无能为力 - 您int拥有 32 位,但 a 的尾数float仅拥有 24 位。四舍五入将会发生。您可以将处理器舍入模式更改为向下舍入而不是最接近,但这会导致一些您想要避免的副作用,特别是如果您在完成后不恢复舍入模式。

您使用的公式没有任何问题,它可以为给定的输入生成最准确的答案。只有一个最终案例未能满足硬性要求。测试特定的最终情况并用满足要求的最接近的值替换它没有错:

if (f >= 1.0f)
    f = 0.99999994f;

0.999999940395355224609375 是 IEEE-754 浮点数在不等于 1.0 的情况下可以取的最接近的值。

于 2013-08-06T16:35:08.823 回答
0

我最终的解决方案是缩小我的常数乘数的大小。这可能是最好的解决方案,因为无论如何乘以双倍是没有意义的。转换为浮点数后看不到精度。

所以2.328306436538696e-010改为2.3283063

于 2013-08-15T16:28:50.847 回答