2015-09-04 87 views
3

我试图弄清楚在C中的某些代码中使用浮点数是否足够满足我的需求,但是在搜索之后并没有真正理解如何将精度转换为实际数字,我决定为我的测试用例写一点代码,看看结果如何。双性能比C漂浮快得多

漂浮看起来足够精确,但我非常惊讶,漂浮物在我的17 4700hq haswell处理器(windows 8.1 x64,C,MSVS v120)上运行时间延长了大约70%。我会预计运行时间会相似或浮动速度更快。但显然不是。所以我关掉了所有的优化,仍然是一样的。尝试在调试版本上,仍然是相同的性能问题。 AVX2和SSE 3都显示了这一点。

双打大概需要197秒的时间才能跑完并且漂浮343秒。我已经浏览了英特尔®64和IA-32架构软件开发人员手册,但考虑到它的规模和缺乏专业知识,我尚未从中收集有关此问题的任何答案。然后我看了看两者的拆卸情况,但我没有注意到我的未经训练的眼睛有明显的差异。

那么,有谁知道为什么会出现这种情况?这里是我用过的代码,除了anError变量之外,唯一的变化是从double到float。

#include <errno.h> 
#include <math.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <string.h> 
#include <time.h> 
#include <sys/types.h> 
#include <omp.h> 



int main(void) { 

    clock_t start = clock() ; 

    // body of a candle 
    double open = 0.500000 ; 
    double close = 0.500001 ; 
    double end = 1 ; 

    uint64_t resultCounter = 0 ; 
    double anError = 0 ; 

    while (open < end){ 
     while(close < end){ 
      //calC# times result is postive. Should be 0. 
      double res = open - close ; 
      if (res > 0) { 
       resultCounter++ ; 
       if (anError < fabs(res)) { anError = res ; } 
      } 
      close = close + 0.000001 ; 
     } 
     open = open + 0.000001 ; 
     close = open + .000001 ; 
    } 

    clock_t finish = clock() ; 
    double duration = ((double) (finish - start))/CLOCKS_PER_SEC; 
    double iterations = (((end - .50000)/.000001) * ((end - .50000)/.000001)) ; 
    fprintf(stdout, "\nTotal processing time was %f seconds.\n", duration) ; 
    fprintf(stdout, "Error is %f. Number of times results were incorrect %llu out of %f iterations.\n", 
     anError, resultCounter, iterations) ; 

    return 0 ; 
} 

编辑:在数字的末尾缺乏f似乎是原因(感谢约阿希姆!)。显然,没有f后缀的float常量实际上是double! C的另一个怪癖,喜欢咬屁股的无知。不知道这种古怪背后的理由是什么,但耸耸肩。如果有人想写出一个好的答案,所以我可以接受它,感觉自由。

+7

在使用'float'代替的程序中,是否记得使用例如'0.000001f'而不是简单的'0.000001'?或者使用['fabsf'](http://en.cppreference.com/w/c/numeric/math/fabs)而不是'fabs'?否则,编译器将不得不添加代码以在“float”和“double”之间转换数字。 –

+0

此外,对于更公平的测试,您应该真正运行多次计算循环,并获得平均值。 –

+0

嗯。如果在消除了隐式转换(使用f *()数学函数)后仍然存在任何差异,则结果将在很大程度上取决于编译器,CPU以及即使编译32位或64位。在具有SSE支持的x86上,SSE寄存器的宽度为128位,因此您可以通过任何方式进行转换。 8位硬件浮点寄存器(现在可能与SSE一起仿真)在80位宽,所以也没有“适合”。 – dhke

回答

0

根据C标准:

一种无后缀浮动常数具有双输入。如果后缀是字母f或F,浮点常量的类型为float。如果后缀是字母l或L,则浮点常量的类型为long double

有关浮点常量的更多详细信息here。所以:

NUM只是一个浮动

float num = 1.0f; 
float num = 1.0F; 

双被转换为float并存储在NUM

float num = 1.0; 

浮子被转换为double,并存储在NUM

double num = 1.0f; 
double num = 1.0F; 

当使用浮点数时由于将常量从double转换为float而涉及到复制内存,所以性能会更差。