如何在 C++ 中设置浮点变量的精度

Posted

技术标签:

【中文标题】如何在 C++ 中设置浮点变量的精度【英文标题】:How do I set the precision of a float variable in c++ 【发布时间】:2022-01-10 05:34:14 【问题描述】:

它将 88.89 打印到控制台,因为 88.888 的百分之一舍入精度等于 88.89。

如何使“b”等于“a”的值四舍五入到百分之一。 (例如 88.89)

我怎样才能让它像这样,| float b = "a" 四舍五入到最接近的百分之一等。

基本上我如何使一个浮点数等于另一个浮点数,但精度较低

示例:

a = 88.888
b = 88.89
c = 88.9

我不希望它打印到控制台,我只想将这些四舍五入的值提供给一个变量,因为我的程序中需要一个四舍五入的数字,而所有其他数字都是小数点后 2 位。如果超过百分之一,它就会退出程序(银行软件,我们没有超过美分的面额。我们基本上只需要百分之一)。

#include<iostream>
#include<iomanip>
using namespace std;
        
int main()
        
    float a = 88.888;
        
    cout << fixed << setprecision(2) << a << endl; 
    
    float b;
        
    return 0;

【问题讨论】:

您不能设置 floatdouble 变量的精度。仅用于此类变量的输入/输出。但是你不应该使用floatdouble 作为货币值开始,因为浮点值/数学是不精确的。请改用非浮点数据类型,例如unsigned int,来表示整美分,而不是一美元的小数。或者,有很多库提供用于货币值的固定精度类型。 因此,如果我理解正确,您总是需要两个小数位,在这种情况下,我建议使用整数,并且在需要输出时始终使用 / 100.0 相关/欺骗:Best way to store currency values in C++ 根据您对此处解决方案的重视程度,请查看C++ decimal data types “我如何使“b”...四舍五入到百分之一” 从字面上理解这个问题,你不能。 floatdouble 类型没有“百分之一”,因为它们使用 2 的幂作为它们的位置,并且没有两个幂的组合可以产生“百分之一”。它只能是近似的。 【参考方案1】:

如何使“b”等于“a”的值四舍五入到百分之一。 (例如 88.89)

根据特定的目标值,您不能。例如,数字 88.89 无法以 32 位 IEEE-754 浮点格式表示,因此您根本无法在该表示中使用具有该值的浮点数。

您可以取而代之的是一个非常接近它的值,例如 88.8899993896484375。如果这是你想要的,那么它是可以实现的。

一个简单的解决方案是使用字符串格式化工具。如您所示,字符流具有称为std::setprecision 的操纵器。只需将浮点数转换为所需精度的字符串,然后将字符串转换回浮点数。

另一个解决方案是使用一点数学。一个直观且看似微不足道的解决方案是:

std::round(a * 100.f) / 100.f;

但是有一个问题。当输入值接近舍入方向改变的阈值时,浮点精度的不足会导致输入值在舍入方向错误的一侧。

例如,最接近 0.005 的可表示值实际上是 0.004999999888241291046142578125,我们希望从 0.005 开始向下取整而不是向上取整。

我们可以通过多使用一位小数的精度并进行中间舍入来解决这个问题:

std::round(
    std::round(a * 1000.f) / 10.f
) / 100.f;

银行软件,我们没有超过美分的面额

我建议不要在银行软件中使用有限精度浮点数。当您不想要更高的精度时,请使用整数来表示美分。当您确实需要更高的精度时,请使用任意精度算术。

【讨论】:

【参考方案2】:

您可能需要什么并不完全清楚,但我假设:

float precision( float f, int places )

    float n = std::pow(10.0f, places ) ;
    return std::round(f * n) / n ;

然后给出:

float a = 8.888 ;

以下内容:

float b = precision( a, 2 ) ;  // 8.89
float c = precision( a, 1 ) ;  // 8.9

但是很明显,虽然 b数学 值是 8.89,默认标准输出方法会这样打印它,但存储的值将 接近 8.89二进制浮点表示允许。例如,虽然:

std::cout << a << '\n' ;
std::cout << b << '\n' ;
std::cout << c << '\n' ;

输出:

8.888
8.89
8.9

设置输出精度揭示了在二进制浮点中表示十进制实数值的局限性:

std::cout << std::setprecision(8) ;
std::cout << a << '\n' ;
std::cout << b << '\n' ;
std::cout << c << '\n' ;

输出:

8.8879995
8.8900003
8.8999996

这很好,可能符合您的要求,但您可能需要注意避免错误。例如,不要期望 c == 8.9true - 期望:

std::cout << ((c == 8.9) ? "equal\n" : "not equal\n") ;

输出not equal。在我的测试中:

std::cout << ((c == 8.9f) ? "equal\n" : "not equal\n") ;

做了equal;但我不会依赖于此。在第一个实例中,转换后的 c 被隐式转换为 double,在该转换中,它的值不同于文字 double 8.9。

当然,一般规则是不比较浮点值是否完全相等。相反,您应该:

std::cout << ((c - 8.9f < 0.01f) ? "equal\n" : "not equal\n") ;

因此,您通常不会将二进制浮点用于银行软件。您将使用十进制浮点数。为此,您需要库支持或其他语言。例如,C#(以及任何 .Net 支持的语言)具有至少 28 位精度的 decimal 类型。

在许多情况下,一般金融应用程序在任何情况下都不应该舍入到最接近的美分 - 例如,股价和货币汇率以美分的小数表示。您只想对最终结果进行四舍五入。

我只想说,除了对存储值进行简单舍入(而不是舍入表示)之外,还有一些问题需要解决。

【讨论】:

std::round(f * n) / n 不是正确的舍入方法,因为f * n 引入了额外的舍入错误。 @EricPostpischil :正如我所说;有问题需要解决。是否重要可能取决于应用程序。如果它确实很重要,那么无论如何这很可能是错误的解决方案。如果您有解决方案,我会对您的解决方案感兴趣。我看了看,但花在这上面的时间比它的价值要多。与以往的浮点一样,您解决了一个问题,然后又出现了另一个问题。

以上是关于如何在 C++ 中设置浮点变量的精度的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C 中设置双精度数据类型变量的精度? [复制]

如何在 BigQuery 架构中设置 FLOAT 的精度

从库 C++ 中设置环境变量

如何在 C++ 中设置进程优先级

如何通过函数在 C++ 中设置私有静态数组?

如何在 Visual Studio (C++) 中设置发布分析