double和float的用法,什么时候用float什么时候用double

首页 > 技术 > 作者:YD1662023-04-16 04:13:52

我记得世纪之初的时候,某本古老的书上有这么一句话,大概是这个意思,无论是float还是double,在CPU内部都是转换为80位浮点数运算的,因此float和double其实是一样快的。

但是时代变化太快,这句话现在还对不对呢?写了个程序验证一下。使用的是Visual C 2015 Update 2,编译为x64架构。为了避免调试器的干扰,直接使用Ctrl F5运行。

程序如下:

// realspeed.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <stdio.h>#include <windows.h>#define veclen 1048576float vec1[veclen];float vec2[veclen];float vec3[veclen];double dvec1[veclen];double dvec2[veclen];double dvec3[veclen];int main(){

Debug下Ctrl F5直接运行:

double和float的用法,什么时候用float什么时候用double(1)

Release下Ctrl F5直接运行:

double和float的用法,什么时候用float什么时候用double(2)

可以看到,在Release编译下,float比double快得多,而在Debug编译下则几乎没有差别。这是为什么呢?在这里我们设置了个断点,进行一下反编译——

Debug下的反编译:

// realspeed.cpp : 定义控制台应用程序的入口点。

Release下的反编译:

// realspeed.cpp : 定义控制台应用程序的入口点。

可以看到,现在早已过了x87 FPU的年代,编译器并没有使用FPU指令,而是使用的SSE指令。

Debug编译下,为了调试方便,将每一个循环都完整表现出来了(循环计数为100000h,即1048576),并且使用了movss/mulss和movsd/mulsd这两组标量指令,速度当然差不多。

而Release编译下,则将循环计数精简为20000h(131072=1048576/8)和40000h(262144=1048576/4),并且使用了movups/mulps和movups/mulpd这两组矢量指令,每次循环内进行2次运算,总计进行40000h(262144=1048576/4)和80000h(524288=1048576/2)次运算。由于SSE寄存器是固定的128位宽,每次只能放置4个32位宽的float或2个64位宽的double数据,因此使用float的话,只需要进行1/4次运算,而使用double的话,则需要进行1/2次运算。

结论就是:对于标量运算,float和double没有显著差别,而对于矢量运算,float比double要快。

因此,在计算量庞大的图形运算中,通常使用float而不是double以提高运算速度。

上面演示的是四则运算,编译器自然有充足的弹性进行优化。那如果是像exp、log这样的math.h函数,编译器怎么优化呢?比如下面的代码:

// realspeed.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <stdio.h>#include <windows.h>#include <math.h>#define veclen 1048576float vec1[veclen];float vec2[veclen];float vec3[veclen];double dvec1[veclen];double dvec2[veclen];double dvec3[veclen];int main(){

我们看一下Release反汇编就知道了。Release反汇编如下:

// realspeed.cpp : 定义控制台应用程序的入口点。

可以看到,编译器并没有调用logf和log函数,而是调用了__vdecl_logf4和__vdecl_log2函数。因此,即使是使用了math.h中的数学函数,仍然可以实现矢量运算优化。

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.