相关疑难解决方法(0)

浮点数学是否破碎?

请考虑以下代码:

0.1 + 0.2 == 0.3  ->  false
Run Code Online (Sandbox Code Playgroud)
0.1 + 0.2         ->  0.30000000000000004
Run Code Online (Sandbox Code Playgroud)

为什么会出现这些不准确之处?

language-agnostic math floating-point floating-accuracy

2798
推荐指数
28
解决办法
28万
查看次数

SSE浮点运算是否可重复?

x87 FPU值得注意的是使用内部80位精度模式,这通常会导致编译器和机器出现意外和不可重现的结果.在我搜索 .NET上可重现的浮点数学时,我发现.NET(Microsoft和Mono)的两个主要实现都在64位模式下发出SSE指令而不是x87.

SSE(2)严格使用32位寄存器用于32位浮点数,严格使用64位寄存器用于64位浮点数.通过设置适当的控制字,可以选择将非正规数刷新为零.

因此,似乎SSE不会受到x87的精度相关问题的影响,并且唯一的变量是可以控制的非正规行为.

抛开超越函数的问题(SSE本身不像x87那样提供),是否使用SSE保证了机器和编译器之间可重现的结果?例如,编译器优化会转化为不同的结果吗?我发现了一些相互矛盾的观点:

如果您有SSE2,请使用它并从此过上幸福的生活.SSE2支持32b和64b操作,中间结果具有操作数的大小.- Yossi Kreinin,http://www.yosefk.com/blog/consistency-how-to-defeat-the-purpose-of-ieee-floating-point.html

...

SSE2指令(...)完全符合IEEE754-1985标准,它们具有更好的可重复性(由于静态舍入精度)和其他平台的可移植性.Muller et aliis, Handbook of Floating-Point Arithmetic - p.107

然而:

此外,您不能将SSE或SSE2用于浮点,因为它太低于指定而不具有确定性.- John Watte http://www.gamedev.net/topic/499435-floating-point-determinism/#entry4259411

.net floating-point sse ieee-754 x87

11
推荐指数
2
解决办法
2348
查看次数

x86和x64之间的浮点算术的差异

我偶然发现了在x86和x64的MS VS 2010版本之间完成浮点算术的方式不同(两者都在同一台64位机器上执行).

这是一个简化的代码示例:

float a = 50.0f;
float b = 65.0f;
float c =  1.3f;
float d = a*c;
bool bLarger1 = d<b;
bool bLarger2 = (a*c)<b;
Run Code Online (Sandbox Code Playgroud)

布尔bLarger1始终为false(在两个版本中d都设置为65.0).变量bLarger2对于x64为false,但对于x86为true!

我很清楚浮点算术和圆角效应正在发生.我也知道32位有时使用不同的指令进行浮动操作而不是64位构建.但在这种情况下,我错过了一些信息.

为什么bLarger1和bLarger2之间首先存在差异?为什么它只出现在32位版本上?

左:x86,右:x64

c c++ floating-point 64-bit visual-studio-2010

10
推荐指数
1
解决办法
6635
查看次数

C循环优化有助于最终分配

因此,对于我在计算机系统课程中的最终作业,我们需要优化这些forloops,使其比原始版本更快.使用我们的linux服务器,基本等级不到7秒,完整等级不到5秒.我在这里的代码大约需要5.6秒.我想我可能需要以某种方式使用指针来使它更快,但我不是很确定.任何人都可以提供我的任何提示或选项吗?非常感谢!

QUICKEDIT:文件必须保持50行或更少,我忽略了教师所包含的那些注释行.

#include <stdio.h>
#include <stdlib.h>

// You are only allowed to make changes to this code as specified by the comments in it.

// The code you submit must have these two values.
#define N_TIMES     600000
#define ARRAY_SIZE   10000

int main(void)
{
    double  *array = calloc(ARRAY_SIZE, sizeof(double));
    double  sum = 0;
    int     i;

    // You can add variables between this comment ...
    register double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0, sum5 = 0, …
Run Code Online (Sandbox Code Playgroud)

c optimization loops compiler-optimization debug-mode

8
推荐指数
2
解决办法
5650
查看次数

标准对于std :: pow,std :: log等cmath函数有什么看法?

标准是否保证函数在所有实现中返回完全相同的结果?

pow(float,float)32位IEEE浮点数为例.如果传入相同的两个浮点数,则所有实现的结果是否相同?

或者是否有一些灵活性,标准允许根据用于实现的算法的微小差异pow

c++ floating-point floating-accuracy ieee-754

6
推荐指数
1
解决办法
280
查看次数

不同语言的浮点精度

我目前正在进行坐标之间的距离计算,并且根据所使用的语言得出的结果略有不同。

计算的一部分是计算cosine给定值的radian。我得到以下结果

// cos(0.8941658257446736)

// 0.6261694290123146 node
// 0.6261694290123146 rust
// 0.6261694290123148 go
// 0.6261694290123148 python
// 0.6261694290123148 swift
// 0.6261694290123146 c++
// 0.6261694290123146 java
// 0.6261694290123147 c
Run Code Online (Sandbox Code Playgroud)

我想尝试并了解原因。如果16dp c四舍五入,过去是唯一的“正确”答案。我感到惊讶的是python,结果有所不同。

目前,这种微小的差异正在被放大,并且超过000个位置增加了不小的距离。

不太确定这是如何重复的。另外,我要求的是整体答案,而不是具体的语言。我没有计算机科学学位。

更新 我接受这可能是一个太宽泛的问题,我想我很好奇为什么我的背景不是CS。我感谢评论中发布的博客链接。

更新2

这个问题源于将服务从移植nodejsgoGo甚至更奇怪,因为距离的总和随多个值而变化,所以我现在无法运行测试。

给定一个坐标列表并计算距离并将它们加在一起,我得到不同的结果。我没有问一个问题,但似乎go会产生不同的结果。

9605.795975874069
9605.795975874067
9605.79597587407
Run Code Online (Sandbox Code Playgroud)

为了完整起见,这里是我正在使用的距离计算:

// cos(0.8941658257446736)

// 0.6261694290123146 node
// 0.6261694290123146 rust
// 0.6261694290123148 go
// 0.6261694290123148 python
// 0.6261694290123148 swift
// 0.6261694290123146 c++
// 0.6261694290123146 java …
Run Code Online (Sandbox Code Playgroud)

c c++ java go rust

6
推荐指数
1
解决办法
250
查看次数

什么是在x86上提供无分支FP min和max的指令?

引用(感谢作者开发和共享算法!):

https://tavianator.com/fast-branchless-raybounding-box-intersections/

由于现代浮点指令集可以在没有分支的情况下计算最小值和最大值

作者的相应代码就是

dmnsn_min(double a, double b)
{
  return a < b ? a : b;
}
Run Code Online (Sandbox Code Playgroud)

我很熟悉例如_mm_max_ps,但这是一个矢量指令.上面的代码显然是用于标量形式.

题:

  • 什么是x86上的标量无分支minmax指令?这是一系列指令吗?
  • 假设它将被应用,或者如何调用它是否安全?
  • 关于min/max的无分支问题是否有意义?根据我的理解,对于光线跟踪器和/或其他视觉软件,给定光线盒交叉例程,分支预测器没有可靠的模式来拾取,因此消除分支确实有意义.我这是对的吗?
  • 最重要的是,所讨论的算法是围绕(+/-)INFINITY进行比较而建立的.这是可靠的,我们正在讨论的(未知)指令和浮点标准吗?

以防万一:我熟悉在C++中使用min和max函数,相信它是相关的,但不是我的问题.

c floating-point optimization x86 assembly

5
推荐指数
1
解决办法
1493
查看次数

不相关的代码会更改计算结果

我们有一些代码会在某些机器上产生意外结果.我把它缩小到一个简单的例子.在下面的linqpad片段中,方法GetValGetVal2实现基本相同,尽管前者还包括对NaN的检查.但是,每个返回的结果都不同(至少在我的机器上).

void Main()
{
    var x = Double.MinValue;
    var y = Double.MaxValue;
    var diff = y/10 - x/10;

    Console.WriteLine(GetVal(x,6,diff));
    Console.WriteLine(GetVal2(x,6,diff));
}

public static double GetVal(double start, int numSteps, double step)
{
    var res = start + numSteps * step;
    if (res == Double.NaN)
        throw new InvalidOperationException();
    return res;
}

public static double GetVal2(double start, int numSteps, double step)
{
    return start + numSteps * step;
}
Run Code Online (Sandbox Code Playgroud)

结果

3.59538626972463E+307
Infinity
Run Code Online (Sandbox Code Playgroud)

为什么会发生这种情况,是否有一种避免它的简单方法?与寄存器有关?

c# floating-point cpu-registers

5
推荐指数
1
解决办法
110
查看次数

为什么Windows VM会产生与Linux不同的浮点输出?

问题

我们有多台运行Ubuntu的机器具有非常相似的规格.我们运行了一个简单的程序来验证我们在Windows VM中看到的每个机器正在运行的问题.在64位Linux计算机上使用gcc 4.8.4编译,在64位Windows VM上使用Visual Studio中的v140编译.

#include <cmath>
#include <stdio.h>

int main()
{
  double num = 1.56497856262158219209;
  double numHalf = num / 2.0;

  double cosVal = cos(num);
  double cosValHalf = cos(numHalf);

  printf("num = %a\n", num);
  printf("numHalf = %af\n", numHalf);
  printf("cosVal(num) = %a\n", cosVal);
  printf("cosValHalf(numHalf) = %a\n", cosValHalf);

  //system("pause");
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

在具有某些CPU的主机上运行相同的二进制文件时会出现此问题.

结果

在Linux上,所有机器都产生相同的输出.在Windows VM上,即使VM版本和设置相同,也会产生不同的结果.另外,每个VM上生成的二进制文件在移动到不同的主机时会产生不同的结果.即在VM2中生成但在LM1上执行的二进制文件返回与VM1生成二进制文件相同的结果.我们甚至复制了虚拟机来确认这种行为,并且确定它仍在继续.

通过上述努力,我认为它不是库差异或VM问题.至于输出,以下CPU产生以下结果:

  • 英特尔®至强(R)CPU E5-2630 0
  • 英特尔®至强(R)CPU E5-2630 v2

以前的CPU在Linux和Windows之间产生统一的结果.结果是十六进制,因为可读性比是否存在差异更重要.

num = 0x1.90a26f616699cp+0
numHalf = 0x1.90a26f616699cp-1
cosVal(num) = 0x1.7d4555e817bdcp-8
cosValHalf(numHalf) = 0x1.6b171bb5e3434p-1
Run Code Online (Sandbox Code Playgroud)

这些CPU在Windows VM上产生的结果与Linux相同:

  • 英特尔®至强(R)CPU E5-2630 v3 …

linux windows precision cpu-architecture virtual-machine

5
推荐指数
1
解决办法
78
查看次数

IEEE-754浮点精度:允许多少错误?

我正在努力将sqrt函数(对于64位双精度数)从fdlibm移植到我目前正在使用的模型检查器工具(cbmc).
作为我的一部分,我阅读了很多关于ieee-754标准的内容,但我认为我不理解基本操作(包括sqrt)的精度保证.

测试我的fdlibm的sqrt端口,我在64位double上使用sqrt进行了以下计算:

sqrt(1977061516825203605555216616167125005658976571589721139027150498657494589171970335387417823661417383745964289845929120708819092392090053015474001800648403714048.0) = 44464159913633855548904943164666890000299422761159637702558734139742800916250624.0
Run Code Online (Sandbox Code Playgroud)

(这个案例在我的测试中打破了关于精度的简单后置条件;我不确定是否可以使用IEEE-754来实现这种后置条件)

为了进行比较,几个多精度工具计算如下:

sqrt(1977061516825203605555216616167125005658976571589721139027150498657494589171970335387417823661417383745964289845929120708819092392090053015474001800648403714048.0) =44464159913633852501611468455197640079591886932526256694498106717014555047373210.truncated
Run Code Online (Sandbox Code Playgroud)

可以看出,左起第17个数字是不同的,意味着如下错误:

3047293474709469249920707535828633381008060627422728245868877413.0
Run Code Online (Sandbox Code Playgroud)

问题1:是否允许这么大的错误?

标准是说每个基本操作(+, - ,*,/,sqrt)应该在0.5 ulps之内,这意味着它应该等于数学上精确的结果四舍五入到最近的fp表示(维基说的是一些库)只保证1 ulp,但目前并不重要).

问题2:这是否意味着,每个基本操作都应该有一个错误<2.220446e-16,64位双精度(机器epsilon)?

我用x86-32 linux系统(glibc/eglibc)计算了相同的结果,并得到了与fdlibm相同的结果,让我想到:

  • a:我做错了什么(但是如何:printf将成为候选人,但我不知道是否可能是这个原因)
  • b:错误/精度在这些库中很常见

floating-point glibc floating-accuracy double-precision ieee-754

4
推荐指数
2
解决办法
3706
查看次数

boost :: random :: uniform_real_distribution应该是跨处理器的相同吗?

以下代码在x86 32位与64位处理器上产生不同的输出.

应该这样吗?如果我用std :: uniform_real_distribution替换它并使用-std = c ++ 11编译它会在两个处理器上产生相同的输出.

#include <iostream>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real_distribution.hpp>

int main()
{
    boost::mt19937 gen;
    gen.seed(4294653137UL);
    std::cout.precision(1000);
    double lo = - std::numeric_limits<double>::max() / 2 ;
    double hi = + std::numeric_limits<double>::max() / 2 ;
    boost::random::uniform_real_distribution<double> boost_distrib(lo, hi);
    std::cout << "lo " << lo << '\n';
    std::cout << "hi " << hi << "\n\n";
    std::cout << "boost distrib gen " << boost_distrib(gen) << '\n';
}
Run Code Online (Sandbox Code Playgroud)

c++ random x86 boost

3
推荐指数
1
解决办法
688
查看次数