c++从零开始---数据类型之浮点数

本文介绍了浮点数的概念、类型及其在C++中的运算特点,包括浮点数的表示方式、有效位数、精度问题以及数据类型之间的转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、浮点数的概念

浮点数可以表示诸如 2.5、3.1415和100033.23 这样的数字,即带小数部分的数字。计算机将这样的值分成两部分储存。一部分表示值,另一部分用于对值进行放大和缩小。比如 3.1415 和 3141.5 除了小数点的位置不同外,其它都是相同的。可以将第一个数表示为0.31415(基准值)和 10 (缩放因子),而将第二个数表示为0.31415 (基准值相同)和 10000 (缩放因子更大)。缩放因子的作用是移动小数点的位置,术语浮点因此得名。
浮点数可以表示非常大和非常小的数值。

浮点数的书写:
第一种常规表达类似 3.14、0.21 和 -14233.02 这样。
第二种是用 e 或者 E 表示。比如 3.14E6 表示 3.14 与1000000 相乘,-0.12e4 表示 -0.12 与10^4(10000)相乘。因此这类表示方法更适合与表示非常大或者非常小的数值。

2、浮点数的类型

c++ 有 3 种浮点类型:float、double 和 long double。这些类型是按照它们可以表示的有效位数和允许的指数最小范围来描述的。有效位是数字中有意义的位。例如,31.4 有效位数是 3 位,其结果精确到了小数点后一位,而 314000 有效位数同样是 3 位,其结果仅仅精确到了千位,其余的 000只是占位。有效位数不依赖于小数点的位置。
c++ 对有效位数的要求:
folat 至少 32 位,double 至少 48 位,且不少于 float,long double 至少和 double 一样多。这三种类型的有效位数可以一样多。此外,通常说 float 的精度一般为小数点后六位或者七位。如果想要更加深入了解计算机是如何计算浮点数范围和精度的,可以参考这里

此外,浮点常量通常为 double 类型,如果希望常量为 float 类型,可以使用 f 或 F 后缀,如果希望使用 long double 类型,可以使用 l 或者 L 后缀(更推荐L)。如 3.14f 或者 3.14L

3、浮点数的优缺点

优点:1、可以表示更多的值;2、可以表示的范围更大。

比如同样为 32 位的 int 和 float 类型,int 能够表示的为 [-2.147483648‬E9, 2.147483647E9],而float 能够表示 [-3.4028235E38, 3.4028235E38]。

缺点:1、运算速度慢;2、精度降低

由于储存空间为改变,两者皆为 32 位,实际上两者能够储存的数字个数是一样多的(实际上 float 能够储存的更少),但是 float 的范围变大了,这是因为缩放因子的原因,但同样导致了 float 的精度降低,在越远离 0 的远端,精度问题越发明显。详细同样可以参照链接

4、c++算术运算符

4.1 运算符符号及用法

  • +:1 + 2 求和
  • - :3 - 2 求差
  • * :3 * 2 求积
  • / :3/2 求商
  • % :3 % 2 求模:生成第一个数除以第二个数后的余数,此处结果为 1。
    注意:% 求模符号的数值只能用整数

另外,对于除法运算,如果两个操作数都是整数,那么结果也为整数,结果的小数部分将被丢弃;如果其中一个为浮点数,那么结果为浮点数。

示例程序如下:

// 算术运算符示例程序.cpp--------------
#include <iostream>
int main()
{
	using namespace std;
	float a = 3.21, b = 2.223;
	int c = 5, d = 2;
	cout << "a + b =: " << a + b << endl;
	cout << "a - b =: " << a - b << endl;
	cout << "a * b =: " << a * b << endl;
	cout << "a / b =: " << a / b << endl;
	cout << "c / d =: " << c / d << endl;
	cout << "c % d =: " << c % d << endl;  
	return 0;
}

4.2 运算符优先级和结合性

  • 算术运算符遵循通常的代数优先级,先乘除,后加减;
  • 可以用“()”来执行自己定义的优先级;
  • 当两个运算符的优先级相同时,c++ 将看操作数的结合性。从左到右的结合性意味着如果两个优先级相同的运算符被同时用于同一个操作数,则首先使用左侧的运算符。从右到左反之。注意:仅当两个运算符都被用于同一个操作数时,优先级和结合性规则才有效。
  • 其它符号的优先级以及结合性参考这里

5、数据类型转换

为了处理潜在的数据混乱,c++ 自动执行很多数据转换:

  • 将一种算术类型的值赋给另一种算术类型的变量时,c++ 将对值进行转换;
  • 表达式包含不同的类型时,c++ 将对其进行转换;
  • 将参数传递给函数时,c++ 将对值进行转换。

以下详细阐明各种转换的方式。

5.1 初始化和赋值进行的转换

c++ 允许将一种类型的值赋给另一种类型的变量。同时,值将被转换为接收变量的类型。比如 a 为 double 类型,b 为 float 类型,执行下列语句:a = b;这样的语句将 b 的值扩展为 double 类型,即一个新的与 b 相同的值,但为 double 类型的值储存在了 a 中,而 b 的值为改变。

将一个值赋给取值范围更大的类型通常不会导致什么问题,比如将 short 值赋给 long 并不会改变这个值,知识占用更多的字节。但是如果将一个很大的 long 值(如 211111111)赋给 float 变量将降低精度,因为 float 只有 6 位有效数字,这个值将被四舍五入为 2.11111E8。

下表列出了一些可能出现的转换问题:

转换潜在的问题
将较大的浮点类型转换为较小的浮点类型,比如将 double 转换为 float精度(有效位数)降低,值可能出现超出目标类型的取值范围,在这种情况下,结果将是不确定的
将浮点类型转换为整型小数部分丢失,原来的值可能超出目标类型的取值范围,在这种情况下,结果将是不确定的
将较大的整型转换为较小的整型,如将 long 转换为 short原来的值可能超出目标的取值范围。

5.2 表达式中的转换

当同一个表达式中包含两种不同的算术类型时,c++ 将执行两种自动转换:首先,一些类型在出现时便会自动转换,其次,有些类型在与其它类型同时出现时将被转换

1、自动转换类型包括,bool、char、unsigned char、signed char 和short 值,他们将被转换为 int。这类转换被称为整型提升。

示例程序如下:

short a = 10;
short b = 20;
short c = a + b;

为执行第 3 行语句,c++ 先取得 a 和 b 的值,然后将它们转换为 short,最后,程序将结果转换回 short,再赋值给 c,因为声明 c 为 short 类型。计算机进行这类操作是因为 int 使用这中类型时,计算速度最快。

2、当不同类型进行计算时,较小的类型将被转换为较大的类型。比如 9.0 除以 5,因为 9.0 是 double 类型,因此在用 5 除之前,程序将 5 转换为 double 类型。

注意:转换只发生在程序计算过程中。变量的值在计算过程中,为方便计算而进行了类型转换,但是计算结束后,变量的类型并未发生改变。

5.3 强制类型转换

表达式:

(typeName) value;
typeName (value);
// 例如:
float (a);  // 将 a 强制转换成 float 类型
<think>嗯,用户问的是C++浮点数计算误差的原因和解决方案。首先,我需要回忆一下浮点数的表示方式,尤其是IEEE 754标准。这部分我记得应该是二进制无法准确表示某些十进制小数,比如0.1这样的数在二进制里是无限循环的,所以存储的时候会有精度丢失。然后,浮点数的存储结构,比如符号位、指数位和尾数位,这些可能也是导致误差的原因。 接下来,用户可能需要具体的例子来理解这个问题。比如,0.1加0.2在C++中会不会像JavaScript那样出现0.30000000000000004这样的结果?可能需要用C++写个小程序验证一下。不过根据引用中的例子,用户提供的C++代码里用了float f=0.1,然后累加多次,结果会有误差。这说明在循环累加的时候,误差会累积,导致结果不准确。 然后是解决方案。用户提供的引用里提到了两种方法,一个是计算误差并在下次循环中减去,另一个是使用分治法来减少误差的积累。分治法可能通过将大次数分解成小次数相加,减少每次的误差累积。另外,引用中还提到了其他语言如JS的处理方法,比如放大成整数计算,或者使用高精度库,这些可能也适用于C++。 另外,IEEE 754标准中的舍入误差也是原因之一。每次运算都可能四舍五入,导致误差积累。比如乘法和除法可能会让误差更明显。用户可能想知道如何避免这些情况,或者有没有现成的库来处理高精度计算。 需要确认C++中有哪些高精度数学库,比如GNU MPFR库,或者Boost库中的相关模块。另外,使用整数代替浮点数,比如将金额以分为单位存储,避免小数运算,这也是常见的解决方案。 可能还需要提到比较浮点数时不要直接用等号,而是使用一个很小的epsilon值来判断是否接近。例如,判断两个浮点数之差的绝对值是否小于1e-6,这样更安全。 总结下来,回答的结构应该是先讲原因,再讲解决方法。原因包括二进制表示问题、存储结构限制和运算中的舍入误差。解决方法包括分治法、误差累计处理、高精度库、整数替代、比较时的epsilon技巧,以及调整运算顺序等。 需要检查是否有遗漏的点,比如是否提到浮点数的精度随着数值大小而变化?比如,当数值非常大时,浮点数的精度会下降,相邻的浮点数之间的差距变大。这可能也是误差的一个原因,但用户的问题可能更关注常见情况下的误差。 另外,用户提供的引用1中的分治法代码可能是一个递归累加的例子,将大次数分解为较小的次数相加,从而减少每次的误差积累。这种方法是否有效,需要进一步解释其原理,比如减少单次累加的次数,从而减少舍入次数,进而降低总误差。 最后,确保回答符合用户的要求,使用中文,行内公式用$...$,独立公式用$$...$$,并且正确引用提供的参考文献。比如在分治法部分引用引用[1],在浮点数标准部分引用引用[4]等。</think>### C++浮点数计算误差原因及解决方案 #### 一、误差产生原因 1. **二进制表示局限** 十进制小数(如$0.1$)在二进制中可能为无限循环小数,导致存储时精度丢失。例如: $$0.1_{10} = 0.0001100110011..._2$$ 存储时会截断为有限位数[^4]。 2. **IEEE 754浮点数结构限制** - float(32位):1位符号,8位指数,23位尾数 - double(64位):1位符号,11位指数,52位尾数 尾数位数限制导致有效数字精度有限,例如float仅能精确表示6-7位十进制数。 3. **运算舍入误差累积** 每次运算(尤其是连续加减)会产生微小误差,多次操作后误差被放大。例如: ```cpp float sum = 0; for (int i=0; i<10000; i++) sum += 0.1; // 结果可能为999.999而非1000 ``` #### 二、解决方案 1. **分治法减少误差累积** 将大规模累加分解为递归小规模操作,降低单次操作次数[^1]: ```cpp float add(float f, int count) { if (count == 1) return f; return add(f, count/2) + add(f, count - count/2); } ``` 2. **误差补偿技术** 记录每次计算的舍入误差,并在后续步骤中补偿: ```cpp float sum = 0, error = 0; for (int i=0; i<N; i++) { float y = value - error; // 补偿误差 float temp = sum + y; error = (temp - sum) - y; // 计算新误差 sum = temp; } ``` 3. **高精度计算库** 使用`GMP`或`Boost.Multiprecision`库实现任意精度运算: ```cpp #include <boost/multiprecision/cpp_dec_float.hpp> using namespace boost::multiprecision; cpp_dec_float_50 a = 0.1, b = 0.2; cout << a + b; // 精确输出0.3 ``` 4. **整数替代法** 将浮点运算转换为整数运算,例如金额计算以分为单位: ```cpp int cents = 100; // 代表1.00元 cents += 30; // +0.30元 ``` 5. **浮点数比较技巧** 避免直接使用`==`,改用误差容忍范围: ```cpp bool isEqual(float a, float b) { return fabs(a - b) < 1e-6; } ``` 6. **调整运算顺序** 先处理数量级相近的数,减少大数"吞没"小数的情况: ```cpp // 不良示例:1e10 + 0.1 - 1e10 ≈ 0 // 优化后:0.1 + (1e10 - 1e10) = 0.1 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值