【问题标题】:double precision on linux using fpu_control.h使用 fpu_control.h 在 Linux 上实现双精度
【发布时间】:2017-07-14 10:14:46
【问题描述】:

我正在尝试将一段特定的代码从 solaris 移植到 Linux。在这个过程中我发现linux上的精度是不同的,它是扩展精度,我们需要明确地将它设置为双精度。为此找到了fpu_control.h库,函数FPU_GETCW和FPU_SETCW函数。但即使在那之后,精度也没有正确设置。 代码sn-p

long double power = 1.0;
#ifdef __linux
    fpu_control_t mask;
        _FPU_GETCW(mask);
mask &= ~(_FPU_EXTENDED & _FPU_SINGLE);
mask |= _FPU_DOUBLE;
        _FPU_SETCW(mask);    

   power *= 0.1;
#endif

当我打印电源时,值是 功率 = 0.1000000000000000055511151231257827

但是我期望 power 的值是 0.1 我在编译时也使用了 -DDouble。有人可以指出我出了什么问题。

【问题讨论】:

  • 我不明白这个。如果你希望power 是一个双精度变量,为什么要声明它为long double?我假设您知道,无论精度如何,0.1 都不能精确地表示为浮点值。
  • 你是对的,但是这种乘法发生在循环内,基于某些条件。 IE。有时我们最终可能会进行 5 次或 3 次乘法运算。所以在每次乘法之后,值都会改变,即。 .1,.01.001, .0001 等基于外部因素。因此代码
  • @girishs 你得到的数字是最接近十进制 0.1 的双精度二进制浮点数。有什么问题?尽可能准确。
  • @art 但是当我打印该值时,它也是一个巨大的数字,如果我稍后使用这个数字进行一些算术运算,我会得到一个不同的数字,对吗?我的期望是我获得与在 Solaris 上获得相同的价值。 (对不起,我可能在这里遗漏了一些东西)
  • @girishs 您应该将有问题的代码显示为 MCVE 以及输入、预期输出和实际输出。

标签: c linux fpu decimal-precision


【解决方案1】:

我期望 power 的值为 0.1

通常不可能满足 OP 的期望。


doublelong double 不能存储所有可能的数字。
double 可以精确编码大约 264 个不同的数字,因为它通常使用 64 bits.
long double 可以精确编码 264、280 或 2128 个不同的数字.

对于typical double,0.1 不能完全编码为double。它不是那些 264 确切数字之一。相反,double x = 0.1 将使用最接近的替代方案初始化 x

Exact value        0.1000000000000000055511151231257827021181583404541015625
OP's printed value 0.1000000000000000055511151231257827

下一个接近的选择是

0.09999999999999999167332731531132594682276248931884765625

这不是 doublelong double 的问题。

【讨论】:

  • 好的,只是为了我的理解。 __SETFPUCW 有什么用?我认为这会将精度从扩展(在linux中,也正如你指出的那样)设置为加倍?这样做不行吗?
  • @girishs 使用__setfpucw。它不会成功。正如这个答案所说,这不是doublelong double 的问题。改变精度无济于事。它可能使一个值“起作用”,但随后其他值将失败。解决方案是“我们需要明确地将其设置为双精度”。是你们都准备好得到double 精度。 IOW,0.1000000000000000055511151231257827 没有错。这就是doubleprecision 应该得到的结果。
【解决方案2】:

您专门请求long double,而您应该想要简单的double。如果您的硬件是 Intel x86/x86-64 CPU,则通过 FPU 的计算以 80 位精度执行。

否则:尝试使用类似 gcc 标志的东西:-mfpmath=sse,这将停止使用 FPU,您的操作将以 64 位(又名双精度)精度执行。

注意:

即使在 Solaris 中,您也很有可能得到 0.1 的不精确表示(没有确切的表示),但值的方式是输出 strong> 通过打印指定数量的十进制数字来隐藏这种不精确性。

【讨论】:

  • 在 32 位上,默认值通常是带有 80 位表示的 x87。在 amd64 上,通常默认为 SSE。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-11
  • 1970-01-01
相关资源
最近更新 更多