问题描述:
根据下面代码和运行结果回答下面的问题。
代码:
// Created by 黑马程序员.
#include "iostream"
#include "windows.h"
using namespace std;
int main()
{
SetConsoleOutputCP(CP_UTF8);
// float单精度浮点数,4字节,6~7位的有效位数
// 有效位数:整数位、小数点、小数位
float num1 = 1234567890; // 只提供了前7位的准确输出
float num2 = 1.234567890; // 只提供了7位的准确输出
cout << fixed; // 设置为小数显示
cout.width(20); // 设置显示的最大宽度(最大位数)
cout << num1 << "," << sizeof(num1) << endl;
cout << fixed; // 设置为小数显示
cout.width(20); // 设置显示的最大宽度(最大位数)
cout << num2 << "," << sizeof(num2) << endl;
// double双精度浮点数,8字节,15~16有效位
double num3 = 1234567890.1234567890;
cout << fixed; // 设置为小数显示
cout.width(20); // 设置显示的最大宽度(最大位数)
cout << num3 << "," << sizeof(num3) << endl;
// long double长精度(多精度)浮点数,16字节,18~19有效位
long double num4 = 1234567890.1234567890;
cout << fixed; // 设置为小数显示
cout.width(20); // 设置显示的最大宽度(最大位数)
cout << num4 << "," << sizeof(num4) << endl;
return 0;
}
运行结果:
D:\Clion_code\cmake-build-debug\Clion_code.exe
1234567936.000000,4
1.234568,4
1234567890.123457,8
1234567890.123457,16
进程已结束,退出代码为 0
问题一:
问题: 请问既然 float只提供前7位的准确输出,那么num1=1234567890包含了10位,分为两部分,前面部分的7位1234567得到了有效输出,后面的3位890没有得到有效的输出。终端的输出结果为res1= 1234567936.000000,res1的前面7位即1234567表示出了num1的前7位,那么res1后面的10位即936.000000是怎么得来的呢?为什么res1前面空出来了3位呢?重点解释936.000000是怎么得来的呢?
回答1:
你的代码中,关于 float
类型的精度和输出,确实会出现一些不符合直观预期的结果。下面将详细解释为什么输出 1234567936.000000
的后面部分是 936.000000
,以及前面空出来的 3 位原因。
1. 浮点数的存储与精度
float
类型在内存中是以二进制浮点数的形式存储的。它的精度约为 6 到 7 位有效数字。这意味着当你赋值一个超过这个范围的数字时,存储的数值可能会由于精度限制而出现误差。
2. num1
的赋值
float num1 = 1234567890; // num1 被赋值为一个超出 float 精度限制的数字
在这里,1234567890
是一个 10 位数。由于 float
的精度限制,实际上只能准确存储前 7 位有效数字,这意味着高位会被保留,而低位可能会发生误差。
3. 计算和输出分析
当你将 1234567890
存储到 num1
时,实际存储的值在计算机内部可能会是一个接近于这个值的数。这个过程中,890
部分并不是简单地被忽略,而是由于浮点数存储的方式,可能会造成误差。在浮点数存储时,由于二进制表示法的限制,某些数字的值会有所偏差。
在这种情况下,1234567890
被存储为一个接近值,如 1234567936
。这就是你在输出中看到的结果。
4. 输出格式
cout << fixed; // 设置为小数显示
cout.width(20); // 设置显示的最大宽度(最大位数)
cout << num1 << "," << sizeof(num1) << endl;
cout.width(20);
设定了输出的总宽度为 20 字符。如果输出的数值不足 20 字符,前面会填充空格以保持格式。- 由于
1234567936.000000
总共占用了 17 个字符,前面会填充 3个空格(总宽度 20),这就是为什么在输出中看到空出来的 3 位。
5. 结论
936.000000
是如何得到的: 它是由于浮点数精度限制导致的误差,当1234567890
被转化为float
时,实际存储的值为1234567936
,而非准确的1234567890
。- 前面空出的 3 位: 这是由于
cout.width(20);
设置的输出宽度造成的,实际输出字符串的宽度不足 20,因此前面填充了空格。
这样的输出在浮点数的表示和输出格式上是非常常见的,理解浮点数的存储方式和输出格式的设置可以帮助你更好地解释和预测这些结果。
###############################################################
问题2:
请问既然 float只提供前7位的准确输出,那么num2=1.234567890包含11位,分为两部分,前面部分的7位1.23456得到了有效输出,后面的4位7890没有得到有效的输出。终端的输出结果为res2= 1.234568,res2的前面7位即1.23456表示出了num2的前7位,那么res2后面的1位即8是怎么得来的呢?为什么res2前面空出来了12位呢?
回答2:
在你的问题中,关于 float
类型的 num2
和其输出的解释,涉及到浮点数的存储精度以及格式输出的行为。让我们逐一分析你的问题。
1. 浮点数的存储与精度
float
类型在内存中只能提供大约 6 到 7 位的有效数字。尽管 num2
被赋值为 1.234567890
,这个数字包含 11 位数字,但 float
只能存储其中的有效部分,剩余部分会由于精度限制而产生误差。
2. num2
的赋值
float num2 = 1.234567890; // num2 被赋值为一个超出 float 精度限制的数字
- 在将
1.234567890
赋值给num2
时,浮点数格式会将其近似为一个可以存储的值,通常是1.234568
。 - 实际存储的值会因为浮点数表示法的限制而导致微小的偏差,因此在输出时可能会显示为
1.234568
。
3. 输出格式分析
cout << fixed; // 设置为小数显示
cout.width(20); // 设置显示的最大宽度(最大位数)
cout << num2 << "," << sizeof(num2) << endl;
cout.width(20);
设置了输出的总宽度为 20 字符。- 由于
1.234568
总共占用了 9 个字符(包括小数点),因此在输出中会有 11 个空格填充到前面,以保证总宽度为 20。
4. 结论
- 后面的 1 位
8
是怎么得来的: 由于浮点数的近似存储,1.234567890
被近似存储为1.234568
,而不是完全的1.234567890
。所以,输出中显示为1.234568
。 - 前面空出来的 12 位: 这是因为
cout.width(20);
指定了输出宽度,而1.234568
占用了 8个字符,因此填充了 12 个空格以达到总宽度 20。
这种输出行为在浮点数的表示和输出格式的设置中是常见的,理解浮点数的存储和输出格式设置将帮助你更好地解释这些结果。
##########################################################
问题3:
请问既然 double只提供前16位的准确输出,那么num3=1234567890.1234567890包含了21位分为两部分,前面部分的16位1234567890.12345得到了有效输出,后面的5位67890没有得到有效的输出。终端的输出结果为res3= 1234567890.123457,res3的前面16位即1234567890.12345表示出了num3的前16位,那么res3后面的1位即7是怎么得来的呢?为什么res3前面空出来了3位呢?
回答3:
在讨论 double
类型浮点数的精度和输出结果时,我们需要理解浮点数的存储方式、有效位数的限制以及输出格式的设置。让我们逐步分析你提到的问题。
1. 浮点数的存储与精度
double
类型在内存中能够提供约 15 到 16 位的有效数字。在你的例子中:
double num3 = 1234567890.1234567890;
虽然 num3
被赋值为 1234567890.1234567890
,这个数字包含了 21 位数字,但由于 double
的精度限制,它只能准确地存储前 15 到 16 位的有效数字。
2. num3
的赋值
- 当你赋值给
num3
时,1234567890.1234567890
将近似为1234567890.123457
,这表明double
类型能处理的精度极限。尽管你提供了更多的数字,存储的结果并不会完全保留。
3. 输出格式分析
cout << fixed; // 设置为小数显示
cout.width(20); // 设置显示的最大宽度(最大位数)
cout << num3 << "," << sizeof(num3) << endl;
cout.width(20);
设置输出的总宽度为 20 字符。1234567890.123457
总共占用了 17 个字符(包括小数点),因此在输出中会有 3 个空格填充在前面,以保证总宽度为 20。
4. 结论
- 后面的 1 位
7
是怎么得来的: 由于浮点数的近似存储,num3
的实际存储值是1234567890.123457
,这是浮点数表示法的结果,它与输入值的最后一部分的影响相结合,导致输出中显示为1234567890.123457
。 - 前面空出来的 3 位: 这是因为
cout.width(20);
指定了输出宽度,而1234567890.123457
占用了 17 个字符,因此填充了 3 个空格以达到总宽度 20。
总结
在处理浮点数时,要理解存储限制和输出格式之间的关系。尽管 double
能够提供更高的有效位数,但由于存储限制和数值范围,最终的输出可能仍然会有所近似。
#######################################################
问题4:
请问既然 long double只提供前16位的准确输出,那么num4=1234567890.1234567890分为两部分,前面部分的16位1234567890.12345得到了有效输出,后面的67890没有得到有效的输出。终端的输出结果为res4= 1234567890.123457,res4的前面16位即1234567890.12345表示出了num4的前16位,那么res4后面的1位即7是怎么得来的呢?为什么res4前面空出来了3位呢?
回答4:
在讨论 long double
类型浮点数的精度和输出结果时,我们需要理解 long double
的有效位数、存储方式以及输出格式的设置。让我们逐步分析你的问题。
1. 浮点数的存储与精度
long double
在大多数编译器中能够提供约 18 到 19 位的有效数字。在你的例子中:
long double num4 = 1234567890.1234567890;
虽然 num4
被赋值为 1234567890.1234567890
,这个数字包含了 21 位数字,但由于 long double
的精度限制,它可能只能准确存储前 18 到 19 位的有效数字。
2. num4
的赋值
- 当你赋值给
num4
时,实际存储的值可能接近1234567890.123457
,这意味着在计算时,long double
的存储和输出会将原始数字四舍五入。
3. 输出格式分析
cout << fixed; // 设置为小数显示
cout.width(20); // 设置显示的最大宽度(最大位数)
cout << num4 << "," << sizeof(num4) << endl;
cout.width(20);
设置输出的总宽度为 20 字符。1234567890.123457
总共占用了 17 个字符(包括小数点),因此在输出中会有 3 个空格填充在前面,以保证总宽度为 20。
4. 结论
- 后面的 1 位
7
是怎么得来的: 这是因为long double
的值实际上是1234567890.123457
。虽然你认为有效部分是1234567890.12345
,但由于浮点数的存储和计算特性,long double
对于尾数的存储和输出可能会将最后的值近似为7
。 - 前面空出来的 3 位: 这是因为
cout.width(20);
指定了输出宽度,而1234567890.123457
占用了 17 个字符,因此填充了 3 个空格以达到总宽度 20。
总结
在处理 long double
浮点数时,要理解存储限制、数值范围和输出格式之间的关系。尽管 long double
提供更高的有效位数,但由于计算精度和四舍五入等因素,最终的输出可能会有所近似。
#########################################################################
问题5:
res1、res2、res3、res4中的小数部分为什么都是只保留了6位呢?小数部分的最后一位是四舍五入来的吗?res1、res3、res4中的整数部分为什么都是只保留了10位呢?float中的整数部分是保留7位,小数部分保留6位,但是精确到5位吗?double中的整数部分是保留10位,小数部分保留6位,但是精确到5位吗?long double中的整数部分是保留10位,小数部分保留6位,但是精确到5位吗?
回答5:
让我们逐个分析 res1
、res2
、res3
和 res4
中的数字格式,了解小数部分和整数部分的保留位数,以及四舍五入的处理。
小数部分的位数
在 C++ 中,默认情况下,cout
对于浮点数的输出会保留 6 位小数。这是因为 cout
默认的精度设置为 6 位小数。这个设置适用于所有类型的浮点数,包括 float
、double
和 long double
。可以通过 cout.precision(n)
来修改这一设置,以改变小数部分的位数。
小数部分的四舍五入
当你输出浮点数时,输出的最后一位通常是经过四舍五入的。例如,若实际计算结果是 1.2345678
,而设置的精度为 6,则输出结果将是 1.234568
,这是经过四舍五入的。
整数部分的位数
对于整数部分的位数,不同的浮点数类型在实际输出时的行为如下:
-
float
: 尽管float
类型的有效位数为 6-7 位,但在这个例子中,由于你赋值的num1
为1234567890
(10 位数字),实际输出的结果1234567936
只显示了前 10 位整数部分。 但由于浮点数的特性,前 7 位有效数字将被准确存储,后面的数字将受限于精度。整数部分会根据浮点数的特性进行一定的近似。 -
double
:double
类型的有效位数通常为 15-16 位。num3
的整数部分1234567890
也是 10 位,由于存储和输出时,前 10 位仍然被准确显示,但小数部分会经过四舍五入。 -
long double
:long double
的有效位数在 18-19 位左右,整数部分仍然是 10 位,四舍五入的结果为1234567890.123457
。
各种类型的总结
-
float:
- 整数部分:通常保留 7 位有效位,但在大于 7 位时,可能只保留前面的 7 位有效位,其他部分会因精度而产生变化。
- 小数部分:保留 6 位,通常为四舍五入。
-
double:
- 整数部分:有效位数约为 15-16 位,但在实际输出中,可能显示的有效位数会因浮点数的存储方式而有限制。
- 小数部分:保留 6 位,通常为四舍五入。
-
long double:
- 整数部分:有效位数约为 18-19 位,实际输出仍为 10 位。
- 小数部分:保留 6 位,通常为四舍五入。
精度与保留位数
总结来说,虽然不同类型的浮点数在有效位数上有所区别,但它们在输出时的表现受限于 cout
的默认设置。默认情况下,cout
保留 6 位小数,整数部分会根据浮点数存储的实际情况显示,不会专门限制为 10 位。实际的四舍五入行为依赖于浮点数的存储和输出精度。
如果需要更精确的控制,可以通过调整 cout.precision()
和 cout.setf()
等设置来改变输出格式。
############################################
问题6:
float有效位数是7位,其中包含了整数位、小数点、小数位。double、long double的有效位都是16位,其中包含了整数位、小数点、小数位。小数部分的无效位有的是四舍五入来的,比如res2、res3小数点后的8、7。但是,float的整数部分的有效位是7个,请问res1中的936.000000几位是什么呢?比如936.000000是如何得来的呢?
问题6回答:
在 float
类型的浮点数中,虽然它的有效位数是 7 位(包括整数部分、小数点和小数部分),但是由于浮点数的表示方式和精度限制,实际输出时可能会产生一些看似不合理的结果。我们来详细分析 res1
中的 936.000000
是如何得到的。
分析 num1
的值
num1
被赋值为 1234567890
,这在 float
类型中会发生截断,具体过程如下:
- 有效位数:
float
的有效位数是 7 位,这意味着在存储1234567890
时,只有前 7 位会被准确存储。 - 数字的存储: 在浮点数存储中,
1234567890
超出了float
的精确表示范围,因此存储时会进行一定的近似。 - 近似过程: 存储的实际值可能会变成一个最接近的
float
类型的值。在这种情况下,1234567890
变成了1234567936
。这个变换是由浮点数的精度限制和计算方式决定的。
关于输出的 936.000000
输出中的 936.000000
来源于以下原因:
- 前 7 位: 根据
float
的有效位数,只能精确显示前 7 位的数字。整数部分的1234567
是有效的,而后面的890
没有被准确存储。 - 最后一位: 在这种情况下,
890
中的8
、9
和0
没有被准确记录,因此存储中可能得到了一个近似的结果,最后的数字变成了936
。这是因为在浮点数的表示中,后面的数位影响了最后存储的值,导致精度损失。
总结
936.000000
是通过浮点数的存储和计算过程得到的:
- 存储
1234567890
时,由于float
只能准确表示前 7 位,导致实际存储值为1234567936
。 - 输出时,整数部分显示为
1234567
,后面的936
是由于浮点数的精度限制和近似计算的结果。
所以,尽管 float
的有效位数是 7 位,但实际输出的内容会受到精度和近似的影响,导致最后的结果看起来与原始值有很大的偏差。这是浮点数在计算机中存储和表示时固有的特性。
#################################################
问题7:
这说明,如果我们输出的数字超过了单精度浮点数或者双精度浮点数时,计算机不会报错,但是会输出一个近似的值,近似值前面几位是有效值,后面几位是近似值转换来的,是吗?
问题7回答:
是的,你的理解是正确的。在使用单精度(float
)或双精度(double
)浮点数时,当输入的数字超过了这些类型能够准确表示的范围时,计算机不会报错,而是会输出一个近似的值。这个近似值的特性如下:
-
有效位和近似位:
- 有效位: 对于单精度浮点数,通常是前 7 位对于整数部分和小数部分的组合;对于双精度浮点数,通常是前 15 至 16 位。
- 近似位: 超出有效位数范围的部分会被视为近似值。计算机会尝试存储一个最接近的数值,而不是精确表示。
-
近似转换:
- 当数值被存储时,计算机会根据浮点数的表示方式和精度限制进行舍入或近似处理。例如,
float
类型可能会将1234567890
近似为1234567936
,这表明存储的值与原始值存在差异。 - 近似值的后几位会受到存储精度的限制和舍入规则的影响,这可能导致输出时的小数部分显示为意外的数字。
- 当数值被存储时,计算机会根据浮点数的表示方式和精度限制进行舍入或近似处理。例如,
-
输出行为:
- 当输出超过浮点数的表示范围时,程序不会报错,但输出的结果可能不是你期望的确切值,而是经过近似计算后的结果。
示例总结
例如,在你的代码中:
float num1 = 1234567890;
存储后,实际值为1234567936
。- 当输出时,虽然浮点数有一定的精度限制,但程序仍然会输出存储的近似值,不会因超出精度而报错。
这就是为什么在处理浮点数时需要注意精度和可能的近似值,尤其是在需要高精度计算的情况下。