【C++】float / double 与 0 值比较

简介: 使用普通的比较没有问题,如果考虑精度的话,需要与具体的值比较

【C++】float / double 与 0 值比较

1. 概述不同


当然使用普通的比较没有问题,如果不考虑精度的话,可以使用

double dvalue = 0.0;
if (0.0 == dvalue)

但是,在某些情况下可能出错。

1.1 - float 与 double 实际存储


floatdouble 在计算机中存储的内容可能与想象中等于代码赋予的字面值不同,如下

float f = 0.1; // f = 0.100000001490116119384765625
double g = 0.1; // g = 0.1000000000000000055511151231257827021181583404541015625

因此与 0 值的比较不可以单纯比较 == 0.0

1.2 - C 语言与 C++ 中不同


然而不仅两种类型不同,单独 double 类型在 C 语言 / C++ 两种语言中也是不同的,比如如下一段代码:

int main ()
{
   
    double a = -1.0e-120;
    if (a < 0.0)
        printf ("%g < 0\n", a);
    if (a > 0.0)
        printf ("%g > 0\n", a);
    if (a == 0.0)
        printf ("%g == 0\n", a);
}

使用 gcc 3.3 编译 C 语言,得到的结果为

-1.0e-120 < 0

而使用同一个编译器 编译 C++ ,得到的结果为

-1.0e-120 == 0

2. 比较方法

这里使用 C++ 的方式进行比较

2.1 - C 风格比较


float,double 分别遵循 R32-24 , R64-53 的标准。网上有一些答案判断 float 的精度误差在 ±1e-6 , double 精度误差在 ±1e-15 之间,示例:

#include <cmath>
#include <cstdio>
#define FLOAT_EPSILON 1e-6
#define DOUBLE_EPSILON 1e-15

int main(int argc, char* argv[])
{
   
    float f = 0.0;
    double d = 0.0;

    if (fabs(f) < FLOAT_EPSILON) {
   
        printf("float value: %g ,",fabs(f));
        printf("is equal to zero!\n");
    } else {
   
        printf("float value: %g ,", fabs(f));
        printf("is not equal to zero!\n");
    }


    if (fabs(d) < DOUBLE_EPSILON) {
   
        printf("double value: %g ,", fabs(d));
        printf("is equal zero!\n");
    } else {
   
        printf("double value: %g ,", fabs(d));
        printf("double not equal to zero!\n");
    }

    return 0;
}

判断一个单精度浮点数:if (fabs(f) <= 1e-6)
判断一个双精度浮点数:if (fabs(f) <= 1e-15)
若在正负范围内,表示等于 0 ,否则,不为 0 。

注:必须包含 \ 头文件,Windows 上创建 Visual Studio 工程,在它的外部依赖项中包含了自身的相似的 VC 头文件,但 Linux 下不显式包含会报错。

但我们不必自己去定义这样一个数,可以包含 C 语言的头文件 float.h

#include <cfloat>

头文件中有定义

// smallest such that 1.0+DBL_EPSILON != 1.0
#define DBL_EPSILON      2.2204460492503131e-016 
// smallest such that 1.0+FLT_EPSILON != 1.0
#define FLT_EPSILON      1.192092896e-07F

最小的增量使得相等判断失效。

DBL_EPSILON 为影响 double 相等比较的最小增量,DBL 表示 double.
FLT_EPSILON 为影响 float 的最小增量 FLT 表示 float.

另有 LDBL_EPSILON 为影响 long double 的最小增量,实际值在 Windows 上与 DBL_EPSILON 一致。

因此代码可以简化为

#include <cmath>
#include <cstdio>
#include <cfloat>
#include <iostream>

int main(int argc, char* argv[])
{
   
    float f = 0.0;
    double d = 0.0;

    if (fabs(f) < FLT_EPSILON) {
   
        printf("float value: %g ,",fabs(f));
        std::cout << "is equal to zero!" << std::endl;
    } else {
   
        printf("float value: %g ,", fabs(f));
        std::cout << "is not equal to zero!" << std::endl;
    }


    if (fabs(d) < DBL_EPSILON) {
   
        printf("double value: %g ,", fabs(d));
        std::cout << "is equal to zero!" << std::endl;
    } else {
   
        printf("double value: %g ,", fabs(d));
        std::cout << "is not equal to zero!" << std::endl;
    }

    return 0;
}

2.2 - 使用 limits 函数


我们可以使用 C++ 标准库中的 <limits> , 使用函数 epsilon 如下图
numeric.png

在具体程序中,我们可以定义两个常量,避免函数重复调用,引起性能损失。

#include <iostream>
#include <limits>
#include <cmath> // 一定要包含

// 定义两个常量
constexpr double dbl_eps = std::numeric_limits<double>::epsilon();
constexpr float flt_eps = std::numeric_limits<float>::epsilon();

int main(int argc, char* argv[])
{
   
    float f = 0.0;
    double d = 0.0;

    if (fabs(f) < flt_eps) {
   
        std::cout << "float value is equal to zero!" << std::endl;
    } else {
   
        std::cout << "float value is not equal to zero!" << std::endl;
    }


    if (fabs(d) < dbl_eps) 
    {
   
        std::cout << "double value is equal to zero!" << std::endl;
    } 
    else 
    {
   
        std::cout << "double value is not equal to zero!" << std::endl;
    }

    return 0;
}

另外为了使用字符串与浮点数互相转换可以使用以下方法:

浮点数转字符串

#include <sstream>
#include <string>

std::string DoubleToString(const double dvalue, int precision)
{
   
    std::stringstream ss;

    ss.precision(precision);
    ss.setf(std::ios::showpoint, std::ios_base::floatfield); // std::ios::showpoint 为了避免截断,如避免 1.000000000452 转为字符串为 1 的情况

    ss << dvalue;

    return ss.str();
}

字符串转浮点数

double StringToDouble(const std::string& str)
{
   
    try 
    {
   
        return std::stod(str);
    }
    catch(...)
    {
   
        std::cerr << "Unable to convert string:" << str << endl;
        return 0.0;
    }
}

3. 参考链接 References


目录
相关文章
|
6月前
|
存储 Java
百度搜索:蓝易云【Java语言之float、double内存存储方式】
由于使用IEEE 754标准进行存储,float和double类型可以表示非常大或非常小的浮点数,并且具有一定的精度。然而,由于浮点数的特性,它们在进行精确计算时可能会存在舍入误差。在编写Java程序时,需要注意使
97 0
|
1月前
|
存储 C语言
使用 sizeof 操作符计算int, float, double 和 char四种变量字节大小
【10月更文挑战第13天】使用 sizeof 操作符计算int, float, double 和 char四种变量字节大小。
82 1
|
4月前
|
存储 编译器 C++
C++从遗忘到入门问题之float、double 和 long double 之间的主要区别是什么
C++从遗忘到入门问题之float、double 和 long double 之间的主要区别是什么
|
4月前
|
存储 SQL 数据库
MySQL设计规约问题之为何推荐用DECIMAL代替FLOAT和DOUBLE来存储精确浮点数
MySQL设计规约问题之为何推荐用DECIMAL代替FLOAT和DOUBLE来存储精确浮点数
|
6月前
|
存储 C语言
计算 int, float, double 和 char 字节大小
计算 int, float, double 和 char 字节大小。
80 3
|
6月前
|
NoSQL C++
c++中包含string成员的结构体拷贝导致的double free问题
c++中包含string成员的结构体拷贝导致的double free问题
103 1
|
6月前
|
C#
C# 字节数组与INT16,float,double之间相互转换,字符数组与字符串相互转换,
C# 字节数组与INT16,float,double之间相互转换,字符数组与字符串相互转换,
183 2
|
6月前
|
C++
[Halcon] 和C++数据之间的转换(HTuple、double、QString等)
[Halcon] 和C++数据之间的转换(HTuple、double、QString等)
701 1
|
存储 C语言
计算 int, float, double 和 char 字节大小
C 语言实例 - 计算 int, float, double 和 char 字节大小。
94 1
|
6月前
|
存储 算法 C++
C++100-C++拓展002-float精度问题
C++100-C++拓展002-float精度问题