Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/158.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何从有序集中返回最近的元素?_C++_Gcc_Floating Point - Fatal编程技术网

C++ 如何从有序集中返回最近的元素?

C++ 如何从有序集中返回最近的元素?,c++,gcc,floating-point,C++,Gcc,Floating Point,我有一个向量,它包含一些浮点值,这些浮点值彼此之间有合理的间隔,并根据一些函数进行排序。比如说, double foo(double x) { return 199.1*x; } double x = 3000000.3157; double y = x + DBL_EPSILON; std::vector<double> s { y,y+10}; std::sort(s.begin(),s.end(),[](double x,double y) { return foo(x

我有一个向量,它包含一些浮点值,这些浮点值彼此之间有合理的间隔,并根据一些函数进行排序。比如说,

double foo(double x)
{
   return 199.1*x;
}
double x = 3000000.3157;
double y = x + DBL_EPSILON;

std::vector<double> s { y,y+10};
std::sort(s.begin(),s.end(),[](double x,double y) { return foo(x) < foo(y) ;} );
并得到不同的位置(对应的值非常不同)

在查看用法时,似乎它们与查找键
k

  • 一组有序浮点值中最近的元素。
  • 一个比率,
    r
    ,它(理想情况下应该是[0,1])附加到连续值
    x1
    x2
    ,使得函数的返回值
    f(x1,x2,r)
    近似等于
    k
它们看起来都是相关的,并且与插值相关。我如何实现它们

注:

在下面的简短代码中

double f1 = foo(x);
double f2 = foo(y);
bool l = foo(x) < foo(y);
std::cout<<std::boolalpha<<(f1<f2)<< " "<<l<<" "<<(f1 == f2) << std::endl;
std::cout << std::boolalpha << (foo(x) < foo(y)) << " "<< (foo(y) < foo(x))
          << " "<<(foo(x) == foo(y) )<<std::endl;
std::cout << std::boolalpha << std::isless(foo(x) , foo(y))
          << " "<< std::isless(foo(y) , foo(x)) <<std::endl;

而我的猜测是,除非我强制GCC存储结果,否则GCC会动态执行更高的精度(80位),从而导致
l
&
的结果不同(f1这两个语句没有任何作用,因为
DBL\u EPSILON
对于这些数字小于1ulp:

double x = 3000000.3157;
double y = x + DBL_EPSILON;
可以肯定的是,我打印了
x
y
的十六进制表示形式,得到了以下结果:

4146E3602868DB8C
4146E3602868DB8C
当我通过两个不同版本的G++(4.4.5和4.8.0)运行问题底部的示例时,同时启用(-O3)和禁用(无标志),我得到以下输出:

false false true
false false true
0 0
我怀疑您看到的行为正是因为您假设的原因:您的编译器对中间结果具有更高的精度,而这正影响到这些比较

您使用的编译器版本是什么,应用程序中的其他代码是否调整了任何舍入模式?您使用的编译标志是什么


编辑1

我可以通过在32位模式下关闭优化重新编译来重现您的行为。在该模式下,我看到编译器将
foo
的结果保留在浮点堆栈上:

_Z3food:
.LFB1053:
    .cfi_startproc
    pushl   %ebp    #
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp  #,
    .cfi_def_cfa_register 5
    subl    $8, %esp    #,
    movl    8(%ebp), %eax   # x, tmp61
    movl    %eax, -8(%ebp)  # tmp61, x
    movl    12(%ebp), %eax  # x, tmp62
    movl    %eax, -4(%ebp)  # tmp62, x
    fldl    -8(%ebp)    # x
    fldl    .LC0    #
    fmulp   %st, %st(1) #,
    leave
这表明这是i386 ABI的一个怪癖。为了验证这一理论,我更仔细地研究了i386 ABI。在(内部页码的缩写为“第3-12页”)上,我找到了可能是吸烟枪的东西:

%st(0)
浮点返回值显示在 浮点寄存器堆栈;在 用浮点表示单精度或双精度值 寄存器。如果函数不返回浮点值, 那么这个寄存器必须是空的。在G之前,这个寄存器必须是空的 函数的入口

它在后面接着说了几段:

浮点返回值显示在Intel387的顶部 注册堆栈。然后调用方必须从 Intel387堆栈,即使它不使用该值 一方履行其义务会导致未定义的程序行为 标准调用序列不包括任何检测此类事件的方法 无法检测返回值类型不匹配。因此 用户必须正确声明所有函数。在 中单精度、双精度或扩展精度值的表示 浮点寄存器。

进一步向下搜索到第3-27页(PDF第53页)和第3-28页(PDF第54页),会出现以下令人困惑的扭曲。图3-30中的表格表明初始舍入模式为“53位(双精度)”,这是进程初始化时的模式

它在下一页进一步给出以下警告:

应小心更改初始浮点状态。在 特别是,许多浮点例程可能会产生未定义的 如果精度控制设置为小于53位,则为 _fpstart例程(参见第6章)将精度控制更改为64位,并设置要询问的所有异常。这是默认状态 符合ANSI C标准和IEEE 754要求 浮点标准

网络上的s表示Linux确实将x87设置为扩展精度(至少在32位ABI中)


编辑2

看来扩展精度确实是罪魁祸首。我在测试用例中添加了以下代码:

添加这些行后,测试用例返回的值与我在64位ABI中看到的值相同

因此,假设您在Linux下编译一个32位程序,这似乎就是您看到奇怪的比较和排序结果的原因


你能像我上面做的那样,在FPU设置为53位精度的情况下重新运行排序和搜索代码,看看这是否解决了你所看到的两个lambda表达式之间的差异吗?

Aha,我能够通过使用
g++-m32
编译来重现这种奇怪的行为。64位编译器没有显示这种奇怪的行为,但是32-b编译器会这样做。现在深入挖掘生成的代码。@user2638924:可能发生的情况是,在
foo(x)
中,
foo(x)
被计算出来,然后以一种消除多余精度的方式临时存储在某个地方(因此,在本例中,产生一个稍小的数字)。然后
foo(y)
被计算。它不会移动到临时位置,并且保留其所有精度。然后将保存的
foo(x)
与原始
foo(y)
进行比较,产生小于的真值。当计算
foo(y)
时,它是
foo(y)
被存储,导致另一个真实结果。@EricPostpischil:BTW,我是JoeZ。我的用户帐户刚才发生了一些事情。希望这只是一个暂时的数据库问题…@EricPostpischil让赋值移除过多精度的原因是否不会使
重新执行仍有争议
false false true
false false true
0 0
_Z3food:
.LFB1053:
    .cfi_startproc
    pushl   %ebp    #
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp  #,
    .cfi_def_cfa_register 5
    subl    $8, %esp    #,
    movl    8(%ebp), %eax   # x, tmp61
    movl    %eax, -8(%ebp)  # tmp61, x
    movl    12(%ebp), %eax  # x, tmp62
    movl    %eax, -4(%ebp)  # tmp62, x
    fldl    -8(%ebp)    # x
    fldl    .LC0    #
    fmulp   %st, %st(1) #,
    leave
void set_fpu (unsigned int mode)
{
      asm ("fldcw %0" : : "m" (*&mode));
}

// ...

set_fpu(0x27F);