CCF-CSP 201903-1 小中大(常见浮点错误)

 

作题时遇到以下几个问题,其中关于浮点类型的存储和转换的问题应该算是比较典型的,在此记录。数组

 

1. 浮点是实数的近似表示:spa

  浮点数在机器内部是由二进制近似表示的(比较常见的格式由IEEE745规定),不是准确值,以下为两个实例:code

  (int)((double)2.3*100) = 229
  (int)((float)2.3*100) = 230blog

 

2. 浮点精度:数学

  float有效数字为7~8位,而double为15~16位。 若超出有效位数,则会发生精度损失。e.g:
  100000000.0f / 211111111.0f = 155555552.0 (ERROR) 
  理想结果本应为155555555.5 (double)
it

 

3. %g占位符: 
  题目要求“对于整数,直接输出整数”。当时个人想法是直接浮点运算,当结果为整数时省略小数点后的0,使其在呈现上如同输出了一个整数。因而利用printf的占位符“%g”输出,但这种作法存在严重的问题:当数据较长或较短时%g会自动改用科学计数法,使得结果出错。 虽然%f不存在该问题,但会输出小数点后的0,一样使结果错误。io

 

具体分析以下class

(1). 首先声明静态存储的数组s用以存储被输入的全部测量数据。题目明确测量数据的绝对值小于等于1e7,因此声明数组元素类型为int便可保证读入数据时不发生溢出;题目明确测量数据最多有1e5个,所以数组的元素个数应很多于1e5。循环

(2). 编写数据输入程序,首先读入第一个整数n,表示测量数据的个数。以后利用循环结构分别将全部测量数据读入以前声明的数组s中,以供后续程序处理。二进制

(3). 编写数据处理程序:

1. 首先要找出最小值与最大值,因为题目明确输入数据有序,即保证升序或降序,故最值必定位于输入数据的首位置和尾位置。从首尾元素中选出极小值,做为整个序列的最小值;同理选出极大值做为整个序列的最大值。

2. 其次求出中位数。由数学可知存在两种状况:如有奇数个数据,则直接选择中间位置的元素值做为中位数;如有偶数个数据,则须要将中间两个元素a,b求均值做为中位数,即

 

由于求均值的过程涉及浮点除法,因此mid引入了浮点结果。题目要求整数与浮点数两种状况应单独输出,因此须要判断结果是否为整数。由等式可知,当(a+b) / 2没有余数时,说明能够整除,此时直接采用整数除法并输出整数结果;当(a+b) / 2有余数时,说明结果为分数,此时再使用浮点除法,并在输出时经过格式控制保留1位小数。

 

 1 #include <cstdio>
 2 #include <algorithm> // max()/min()
 3 
 4 using namespace std;
 5 
 6 static int s[100000+2];
 7 
 8 /*
 9  * 1. 占位符: 
10  * %g 在数据较长或较短时会采用%e输出,即采用科学计数法。 
11  * 而 %f则不存在该问题,但输出小数点后多余的0。  
12  * 2. float精度:
13  * float有效数字为7~8位,而double为15~16位。 若超出有效位数,则精度丢失。e.g:
14  * 100000000.0f / 211111111.0f = 155555552.0(ERROR) 
15  *                               155555555.5 (double)
16  * 3. 浮点向整数转换的精度丢失问题:
17  * (int)((double)2.3*100) = 229(ERROR)
18  * (int)((float)2.3*100) = 230(?)
19  *
20  * 4. 同3,整数向浮点转换:浮点存储的是近似值,可能形成整数数值变化。 (2问题之根源) 
21  */
22 int main(void) {
23     int n;
24     scanf("%d", &n);
25     for(int i=0; i <n; i++) {
26         scanf("%d", &s[i]);
27     }
28     
29     int n_max = max(s[0], s[n-1]);
30     int n_min = min(s[0], s[n-1]);
31     
32     if (n & 1) {
33         printf("%d %d %d\n", n_max, s[n/2], n_min);
34     } else {
35         if ( (s[n/2-1]+s[n/2]) & 1 )
36             printf("%d %.1lf %d\n", n_max, (double)(s[n/2-1]+s[n/2])/2.0, n_min);
37         else
38             printf("%d %d %d\n", n_max, (s[n/2-1]+s[n/2])/2, n_min);
39     }
40     
41     return 0;
42 }
相关文章
相关标签/搜索