http://bailian.openjudge.cn/practice/1001/
对数值很大、精度很高的数进行高精度计算是一类十分常见的问题。比如,对国债进行计算就是属于这类问题。
现在要你解决的问题是:对一个实数R( 0.0 < R < 99.999 ),要求写程序精确计算 R 的 n 次方(Rn),其中n 是整数并且 0 < n <= 25。
Input
输入包括多组 R 和 n。 R 的值占第 1 到第 6 列,n 的值占第 8 和第 9 列。
Output
对于每组输入,要求输出一行,该行包含精确的 R 的 n 次方。输出需要去掉前导的 0 后不要的 0 。如果输出是整数,不要输出小数点。
Sample Input
95.123 12
0.4321 20
5.1234 15
6.7592 9
98.999 10
1.0100 12
Sample Output
548815620517731830194541.899025343415715973535967221869852721
.00000005148554641076956121994511276767154838481760200726351203835429763013462401
43992025569.928573701266488041146654993318703707511666295476720493953024
29448126.764121021618164430206909037173276672
90429072743629540498.107596019456651774561044010001
1.126825030131969720661201
思路:本题采用C和Java来做,java提供了BigDecimal类,对于该问题处理起来很方便。
C主要思想是先确定小数点的位置,然后把其转换为整数,再用一个数组来计算每一位数与要乘法数相乘的结果,完成乘方的运算。
#include <stdio.h>
#include <string.h>
int len; //把小数点后化为整数,乘方后的总长度
int product[126] = {0}; // 最大可能长度 5*25 + 1 = 126
void multiply(int a[], int n)
//n是把小数点后化为整数的乘方数,a是存储结果的数组
{
int i;
int carry = 0; // a carry number in multiplying
for (i = 0; i < len; i++)
{
int temp = a[i]*n + carry;
a[i] = temp % 10;
carry = temp / 10;
}
while (carry)
{
a[i++] = carry % 10;
carry /= 10;
}
len = i; //i最后为整个结果有效数字的长度。
}
int main()
{
int n; // power n
char s[6]; // real number R, at most the length is 6
while (scanf("%s %d", s, &n) != EOF)
{
int position=0, i=0, num=0, j=0;
for (i=0; i<strlen(s); i++)
{
if (s[i] == '.')
{
position = (strlen(s) - 1 - i) * n; // 计算从末尾开始数,小数点的位数
}
else
{
num = num*10 + s[i] - 48; // 将小数部分转换为整数
}
}
// product calculation
product[0]=1;
len = 1;
for (i = 0; i < n; i++)//开始运算n次乘法
{
multiply(product, num);
}
// format output
if (len <= position) // 结果小于1时,把小数点后的0打出来
{
printf("."); // print decimal point
for (i=0; i<position-len; i++)
{
printf("0"); // print zero between decimal point and decimal
}
j = 0;
//while (product[j] == 0) // trim trailing zeros
//{
// j++;
//}
for (i=len-1; i>=j; i--)
{
printf("%d", product[i]);
}
}
else//结果大于1时
{
j=0;
while (product[j]==0 && j<position) // trim trailing zeros
{
j++;
}
for (i=len-1; i>=j; i--)
{
if (i+1 == position) // cause index in C language starts from 0
{
printf(".");
}
printf("%d", product[i]);
}
}
printf("\n");
}
}
下面是java的源码:
import java.util.Scanner;
import java.math.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
BigDecimal bd = new BigDecimal(sc.next());
BigDecimal result = bd.pow(sc.nextInt());
String s = result.stripTrailingZeros().toPlainString();
if(s.startsWith("0"))
s=s.substring(1);
System.out.println(s);
}
}
}
java.util.Scanner是Java5的新特征,主要功能是简化文本扫描。这个类最实用的地方表现在获取控制台输入,其他的功能都很鸡肋,尽管Java API文档中列举了大量的API方法,但是都不怎么地。
当通过new Scanner(System.in)创建一个Scanner,控制台会一直等待输入,直到敲回车键结束,把所输入的内容传给Scanner,作为扫描对象。如果要获取输入的内容,则只需要调用Scanner的nextLine()方法即可。
next()一定要读取到有效字符后才可以结束输入,对输入有效字符之前遇到的空格键、Tab键或Enter键等结束符,next()方法会自动将其去掉,只有在输入有效字符之后,next()方法才将其后输入的空格键、Tab键或Enter键等视为分隔符或结束符。简单地说,next()查找并返回来自此扫描器的下一个完整标记。完整标记的前后是与分隔模式匹配的输入信息,所以next方法不能得到带空格的字符串而nextLine()方法的结束符只是Enter键,即nextLine()方法返回的是Enter键之前的所有字符,它是可以得到带空格的字符串的。
**注意:**double nextDouble() , float nextFloat() , int nextInt() 等会将enter等留在输入队列中,所以如果此时再调用nextLine()就会直接把enter当作结束符结束,因此什么都没有读到。如果要混用next和nextLine可以在调用完next后自动调用一个nextLine。
java.math.BigDecimal.stripTrailingZeros() 返回一个BigDecimal,它在数值上等于这一个,但表示形式移除所有尾部零。
toString和toPlainString的区别就是前者会转换成科学计数法,后者不会。
对于 BigDecimal b ; (b=(0.4321)^ 20)
String s = b.toPlainString() ;
System.out.println(s) ;
输出为:
0.00000005148554641076956121994511276767154838481760200726351203835429763013462401
若String s = b.toString() ;
输出为:
5.148554641076956121994511276767154838481760200726351203835429763013462401E-8
public String substring(int beginIndex, int endIndex)
第一个int为开始的索引,对应String数字中的开始位置,
第二个是截止的索引位置,对应String中的结束位置
1、取得的字符串长度为:endIndex - beginIndex;
2、从beginIndex开始取,到endIndex结束,从0开始数,其中不包括endIndex位置的字符