仅使用处理单个数字的I/O例程,编写一个过程以输出任意实数(可以是负的)。

题目取自:《数据结构与算法分析:C语言描述_原书第二版》——Mark Allen Weiss    

练习1.3 如题。

补充说明:假设仅有的I/O例程只处理单个数字并将其输出到终端,我们将这个例程命名为PrintDigit;例如"PrintDigit(4)"

     将输出一个"4"到终端。

思路:根据先简后繁的原则,程序各版本完成的功能依次为:处理正整数—>处理所有整数—>处理double—>double舍入。

版本一:

// 正整数版(更大的范围可以使用long long int)
#include<stdio.h>

void PrintOut(int number);
void PrintDigit(int number);

int main(void)
{
	int n = 123;

	PrintOut(n);

    return 0;
}

void PrintOut(int number)
{
	int value = number / 10;

	if(value != 0) // 考虑用while会出现什么情况,如果数字不是个位数,那么程序死循环输出首位数字。
		PrintOut(value);

	PrintDigit(number % 10);
}

void PrintDigit(int number) // 对处理单个数字的I/O例程进行模拟
{
	printf("%d", number);
}

版本二:

// 强化版:所有整数
#include<stdio.h>

void PrintOut(int number);
void PrintDigit(int number);
void PreDispose(int number); // 对传入的参数做一些预处理工作,然后调用PrintOut函数
int main(void)
{
	int n = -45689;

	PreDispose(n);

    return 0;
}

void PreDispose(int number)
{
	if(number < 0)
	{
		putchar(‘-‘);
		number = -number;
	}

	PrintOut(number);
}

void PrintOut(int number)
{
	int value = number / 10;

	if(value != 0)
		PrintOut(value);

	PrintDigit(number % 10);
}

void PrintDigit(int number)
{
	printf("%d", number);
}

讲述版本三之前先来看一下double类型在内存中的存储情况,在code::blocks中定义如下变量:

设置断点,调试,a、b、c初始化之前的值如图一,赋值后的值如图二

        图一                    图二

不难看出,double类型在内存中存储是有误差的。比如我们定义的c = 9.1,内存中实际值为9.099999999...6。其实这个值也是四舍五入得来的,那么如何看到它在内存中的全貌呢,请看版本三:

版本三:

// 强化版二:double类型
#include<stdio.h>
#include<math.h>

void PrintOut(int number);
void PrintDigit(int digit);
void PreDispose(double number); 

int main(void)
{
	double n = 9.1;

	PreDispose(n);

    return 0;
}

void PreDispose(double number)
{
	double ip;

	// 函数modf把传入的第一个参数分为整数和小数两部分,整数部分保存在第二个参数中
	// 两部分的正负号均匀x相同,该函数返回小数部分
	double fraction = modf(number, &ip);
	//	一个更加简便的分离小数位与整数位的方法如下:
	// double ip, fraction;    
	// fraction = number - (int)number;   
	// ip = (int)number;	

	if(ip < 0)
	{
		putchar(‘-‘);
		ip = -ip;
		fraction = -fraction;
	}

	PrintOut(ip);
	putchar(‘.‘);

	// 对小数部分逐位输出,理论上可以输出到小数点后任意多的数位,就这几行代码还耗了不少脑细胞呢Orz
	int N = 70; // 希望输出到小数点多少位,就设定N为多少(想不到更好的解释了:-)
	while(N--)
	{
		fraction *= 10;
		PrintOut((int)fraction%10);	// 因为传入的参数是单个数字,所以这里也可以直接调用PrintDigit函数
		fraction = fraction - (int)fraction; // 防止fraction数据过大,导致整型溢出
	}
}

void PrintOut(int number)
{
	int value = number / 10;

	if(value != 0)
		PrintOut(value);

	PrintDigit(number % 10);
}

void PrintDigit(int digit)
{
	printf("%d", digit);
}
//输出结果:9.0999999999999996447286321199499070644378662109375000000000000000000000

对b=1.1而言,输出结果为:1.1000000000000000888178419700125232338905334472656250000000000000000000,对比图二不难得出结论:code::blocks中所示的数值就是原double值四舍五入并且精确到小数点后16位得到的。

但是,存在一个问题,比如拿c=9.1来说事,我们令版本三中程序中的变量N的值为1,则输出结果为9.0。所以这个程序的一个问题就是:没有考虑舍入误差。那么如何处理舍入误差呢,还好有Weiss 提供的core->)。

版本四:终极进化版

//下面是我根据作者提供的核心代码补全后的版本,考虑了舍入误差(四舍五入)
#include<stdio.h>

int IntPart(double N); // 得到N的整数部分
double DecPart(double N); // 得到N的小数部分
void PrintReal(double N, int DecPlaces); // 该函数打印double值,其中第二个参数为精确到小数点后的位数
void PrintFractionPart(double FractionPart, int DecPlaces); // 打印小数部分
double RoundUp( double N, int DecPlaces ); // 实现四舍五入的函数
void PrintOut(int number);
void PrintDigit(int number);

int main(void)
{
	double value = -9.1;

	PrintReal(value, 1); 

    return 0;
}

double RoundUp( double N, int DecPlaces ) // 窃以为该函数为整个程序的画龙点睛之笔。
{
	int i;
	double AmountToAdd = 0.5;

	for( i = 0; i < DecPlaces; i++ )
		AmountToAdd /= 10;
	return N + AmountToAdd;
}

void PrintReal(double N, int DecPlaces)
{
	int IntegerPart;
	double FractionPart;

	if( N < 0 )
	{
		putchar(‘-‘);
		N = -N;
	}

	N = RoundUp(N, DecPlaces);
	IntegerPart = IntPart( N );
	FractionPart = DecPart( N );

	PrintOut(IntegerPart); // 假设错误检查已经完成,即输入是常规的文本
	if(DecPlaces > 0)
		putchar(‘.‘);
	PrintFractionPart(FractionPart, DecPlaces);
}
void PrintFractionPart(double FractionPart, int DecPlaces) // 程序三中输出小数部分的实现思路与之类似,不过其提供了对外接口——函数外部可以设定要输出的小数位数
{
	int i, Adigit;

	for( i = 0; i < DecPlaces; i++ )
	{
		FractionPart *= 10;
		Adigit = IntPart(FractionPart);
		PrintDigit(Adigit);

		FractionPart = DecPart(FractionPart);
	}
}

int IntPart(double N)
{
	return (int)N;
}

double DecPart(double N)
{
	return N - IntPart(N);
}

void PrintOut(int number)
{
	int value = number / 10;

	if(value != 0)
		PrintOut(value);

	PrintDigit(number % 10 );
}

void PrintDigit(int digit)
{
	printf("%d", digit);
}
//输出结果:9.1

可以看到,该程序不仅解决了版本三中遗留的小数点舍入问题,而且通过设定PrintReal函数的第二个参数的值为70可以得到和版本三中相同的结果。End。

—————————————————————————^_^我是分隔线^_^—————————————————————————

All Rights Reserved.

Author:海峰:)

Copyright © xp_jiang.

转载请标明出处:http://www.cnblogs.com/xpjiang/p/4135919.html

时间: 2024-10-10 15:25:07

仅使用处理单个数字的I/O例程,编写一个过程以输出任意实数(可以是负的)。的相关文章

正则表达式:匹配单个数字重复n次

匹配单个数字重复n次:(\d)\1{n-1}其中,\d表示一位数字,(\d)表示匹配之后捕获该匹配,并分组并对组进行编号\1表示被捕获的第一个分组{n-1}是因为被捕获的第一个分组已经消耗了一位数字,因此要减1. 注意:用python写的时候,模式字符串引号前面一定要加r, 即应该写成: re.findall(r'(\d)\1{3}','11112222')而不是 re.findall('(\d)\1{3}','11112222'),否则会匹配不到任何结果. 原文地址:https://www.c

python学习之 plt 基础学习 python学习画图 注意: x,y 是list 不是单个数字

注意: x,y 是list  不是单个数字 plt.plot(x,y) plt.plot(x,y,format_string,**kwargs) x轴数据,y轴数据,format_string控制曲线的格式字串 format_string 由颜色字符,风格字符,和标记字符 import matplotlib.pyplot as plt plt.plot([1,2,3,6],[4,5,8,1],’g-s’) 接触到 np.linspace方法: x=np.linspace(-1,1,5)y=2*x

【C语言】编写一个函数,将一个数字字符串转换成该字符串对应的数字(包括正整数、负整数)

/* 编写一个函数,将一个数字字符串转换成该字符串对应的数字(包括正整数.负整数) 例如:"12" 返回12 "-123" 返回-123 函数原型:int my_atof(char *str) */ #include <stdio.h> int my_atof(char *str) { int flag=0; int m=0; if(*str=='-') { flag=1; str++; } while(*str!='\0') { if(*str<

有1、2、3、4四个数字,能组成多少个互不相同且一个数字中无重复数字的三位数?并把他们都输出。

/** * 有1.2.3.4四个数字,能组成多少个互不相同且一个数字中无重复数字的三位数?并把他们都输出. * */ public class Test1 { public static void main(String[] args) { int num = 0, c = 0; for (int i = 1; i <= 4; i++) { for (int j = 1; j <= 4; j++) { for (int k = 1; k <= 4; k++) { if (i != j &

需求:有一个猜数字小游戏,请写一个程序实现在测试类中只能使用5次,超过5次提示:游戏试玩结束,请付费。

package cn.idcast4; import java.io.FileNotFoundException;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;import java.io.Reader;import java.io.Writer;import java.util.Properties; /* * 需求:有一个猜数字小游戏,请写一个程序实现在测试类中只能使用5次, *

编写一个猜数字游戏

编写一个程序,实现猜数字游戏,计算机随机产生一个数,输入猜的数,与计算机随机产生的数进行比较,当猜的数大于随机产生的数,给出提示猜的数过大,反之,给出提示猜的数太小.下面是具体的程序: #include <stdio.h> #include <stdlib.h> #include <time.h> void fun()        //定义fun函数,说明游戏的具体操作 {     int a=0;     int ret=rand()%100;           

【C语言】求旋转数组的最小数字,输入一个递增排序的数组的一个旋转,输出其最小元素

//求旋转数组的最小数字,输入一个递增排序的数组的一个旋转,输出其最小元素 #include <stdio.h> #include <string.h> int find_min(int arr[],int len) { int i = 0; for (i = 1; i < len; i++) { if (arr[i] < arr[0]) return arr[i]; } return arr[0]; } int main() { int i; int arr1[] =

课后作业2——编写一个程序,此程序从命令行接收多个数字,求和之后输出结果。

1.题目:编写一个程序,此程序从命令行接收多个数字,求和之后输出结果. 2.程序设计思想:由于命令行的参数均为字符串类型,所以要将字符串类型转换为整型,然后再进行数字的简单加和.求和运用for循环实现. 3.源程序://从命令行接收多个数字,求和之后输出结果.//20153291  冯怡晨public class SumResult {    public static void main(String[] args) { int sum=0; System.out.println("数字:&q

华为 2015 机试 输出:数字后面的连续出现的(2个或多个)相同字符(数字或者字符),删去一个,非数字后面的不要删除,例如,对应输出为:33aabb55pin。

1 package 华为机试; 2 //C++ 输入:由数字和字母组成的字符串,例如:333aaabb55ppin 3 //输出:数字后面的连续出现的(2个或多个)相同字符(数字或者字符),删去一个,非数字后面的不要删除,例如,对应输出为:33aabb55pin. 4 5 //这句话的核心就是在字符串删除一些字符,感觉处理很复杂,删除哪些字符呢?我们观察发现, 本字符串中删除了一个3,一个a,一个p,满足的规则是啥呢? 333中删除最后一个3,3aa删除了一个a,5pp中删除一个p, 6 //规