躲不掉的高精度计算,蓝桥杯必考

目录

为什么用高精度?

java提供的高精度类型

提供的方法

拿例题说话


为什么用高精度?

举个栗子,如果用Java语言写个程序来计算138651223773355+786437100。

使用int类型肯定不行,因为int的范围是-2147483648 到2147483647之间。使用long类型可以解决这个问题,但是如果要求138651223773355*786437100的值呢?long类型也是不够用的,long的范围是-9223372036854775808到+9223372036854775807之间,所以我们需要一个范围足够大的类型来计算。

java提供的高精度类型

  • 高精度整数BigInteger
  • 高精度浮点数BigDecimal

API中的描述BigInteger

不可变的任意精度整数。所有操作的行为就好像大整数是用二进制补码表示的(就像Java的基本整数类型一样)。BigInteger提供了Java所有原始整数运算符的类似物,以及java.lang.Math的所有相关方法。此外,BigInteger还提供了模块化算术、GCD计算、素性测试、素生成、位操作和一些其他杂项操作的操作。
算术运算的语义完全模仿了Java语言规范中定义的整数算术运算符的语义。例如,除以零会引发算术异常,将负数除以正数会产生负数(或零)余数。规范中关于溢出的所有细节都被忽略了,因为大整数被做得足够大以适应操作的结果。

移位操作的语义扩展了Java的移位操作符的语义,允许负的移位距离。具有负偏移距离的右偏移导致左偏移,反之亦然。省略了无符号右移运算符(> >),因为该操作与该类提供的“无限字长”抽象相结合没有什么意义。

按位逻辑运算的语义完全模仿了Java的按位整数运算符。二进制运算符(and、or、xor)在执行运算之前会隐式地对两个操作数中较短的一个执行符号扩展。

比较操作执行有符号整数比较,类似于Java的关系运算符和等式运算符。

提供模算术运算来计算余数、执行幂运算和计算乘法逆。这些方法总是返回一个介于0和(modulus)之间的非负结果。

位操作对其操作数的二进制补码表示的单个位进行操作。如有必要,操作数会进行符号扩展,以便包含指定的位。没有一个单比特操作可以产生一个符号与正在操作的大整数不同的大整数,因为它们只影响一个比特,并且这个类提供的“无限字长”抽象确保了每个大整数前面有无限多的“虚拟符号比特”。

为了简洁明了,在对BigInteger方法的描述中使用了伪代码。伪代码表达式(i + j)是“一个BigInteger,它的值是BigInteger i的值加上BigInteger j的值”的简写形式。伪代码表达式(i == j)是“当且仅当BigInteger i表示与BigInteger j相同的值时为真”的简写形式。其他伪代码表达式的解释类似。

当传递任何输入参数的空对象引用时,此类中的所有方法和构造函数都会引发NullPointerException。

API中的描述BigDecimal

不可变的任意精度带符号十进制数。BigDecimal由一个任意精度的整数小数位数和一个32位整数小数位数组成。如果为零或正,刻度是小数点右边的位数。如果为负,则该数字的未缩放值乘以10的比例的负幂。因此,用大十进制表示的数值是(无标度值× 10标度)。
BigDecimal类提供算术、比例操作、舍入、比较、散列和格式转换等操作。toString()方法提供了BigDecimal的规范表示。

BigDecimal类让用户可以完全控制舍入行为。如果未指定舍入模式,并且无法表示确切的结果,则会引发异常;否则,可以通过向操作提供适当的MathContext对象,按照选定的精度和舍入模式进行计算。在任一种情况下,都提供了八种舍入模式来控制舍入。用这个类中的整数字段(如ROUND_HALF_UP)来表示舍入模式,在很大程度上已经过时;RoundingMode枚举的枚举值(如RoundingMode。应该改为使用HALF_UP)。

当数学上下文对象的精度设置为0时(例如,数学上下文。无限制),算术运算是精确的,就像没有MathContext对象的算术方法一样。(这是5之前版本中支持的唯一行为。)作为计算精确结果的必然结果,不使用精度设置为0的MathContext对象的舍入模式设置,因此不相关。在除法的情况下,精确的商可以有无限长的十进制展开;例如,1除以3。如果商具有非终止的十进制扩展,并且指定该操作返回精确的结果,则会引发算术异常。否则,将返回除法的确切结果,就像对其他操作所做的那样。

当精度设置不为0时,BigDecimal算法的规则与ANSI X3.274-1996和ANSI X3.274-1996/AM 1-2000(第7.4节)中定义的算法的选定操作模式大致兼容。与这些标准不同,BigDecimal包含许多舍入模式,在5之前的BigDecimal版本中,舍入模式是除法的必备条件。这些ANSI标准和BigDecimal规范之间的任何冲突都将通过支持BigDecimal来解决。

由于相同的数值可以有不同的表示(不同的比例),算术和舍入规则必须指定数值结果和结果表示中使用的比例。

通常,舍入模式和精度设置决定了当精确结果的位数(在除法的情况下可能无限多)多于返回的位数时,运算如何返回有限位数的结果。首先,返回的总位数由MathContext的精度设置指定;这决定了结果的精度。数字计数从精确结果最左边的非零数字开始。舍入模式决定了任何丢弃的尾随数字如何影响返回的结果。

对于所有算术运算符,执行运算时就像首先计算精确的中间结果,然后使用选定的舍入模式舍入到精度设置指定的位数(如有必要)。如果没有返回确切的结果,则丢弃确切结果的一些数字位置。当舍入增加返回结果的幅度时,进位传播到前面的“9”位就有可能创建一个新的数字位置。例如,将值999.9舍入到三位数,舍入后的数字等于一千,表示为100×101。在这种情况下,新的“1”是返回结果的前导数字位置。

提供的方法

1.如何赋值?

        BigInteger b=new BigInteger("123");

 BigInteger里的参数是字符串的形式,原则上是可以表述一个无限大的数。

2.常用方法:

  • BigInteger abs() 返回大整数的绝对值
  • BigInteger pow(int exponent) 返回当前大整数的exponent次方
  • String toString() 将当前大整数转换成十进制的字符串形式
  • BigInteger remainder(BigInteger val) 返回当前大整数除以val的余数
  • BigInteger add(BigInteger val) 返回两个大整数的和
  • BigInteger subtract(BigInteger val)返回两个大整数相减的结果
  • BigInteger multiply(BigInteger val) 返回两个大整数的积
  • BigInteger divide(BigInteger val) 返回两个大整数的商

在这里还是要说一下除法

BigDecimal.divide(BigDecimal divisor, int scale, RoundingMode roundingMode) ;

如:

        BigDecimal b1 = new BigDecimal("123.564");
        BigDecimal b2 = new BigDecimal("3.3");
        BigDecimal divide = b1.divide(b2, 10, RoundingMode.HALF_UP);

 10表示的是精确到小数点后几位

ROUND_CEILING:舍位时往正无穷方向移动   
正数:1.1 -> 2   1.5-> 2   1.8-> 2   
负数:-1.1-> -1   -1.5-> -1   -1.8-> -1
ROUND_DOWN:向0的方向移动
正数:1.1-> 1   1.5-> 1   1.8-> 1   
负数:-1.1-> -1   -1.5-> -1   -1.8> -1
ROUND_FLOOR:与CEILING相反,往负无穷   
正数: 1.1-> 1   1.5-> 1   1.8-> 1   
负数: -1.1-> -2   -1.5-> -2   -1.8-> -2
ROUND_HALF_DOWN:以5为分界线,或曰五舍六入
正数:1.5-> 1   1.6-> 2  
负数:-1.5-> -1   -1.6-> -2  
ROUND_HALF_EVEN:同样以5为分界线,如果是5,则前一位变偶数
1.15-> 1.2   1.16-> 1.2   1.25-> 1.2   1.26-> 1.3
ROUND_HALF_UP:最常见的四舍五入
ROUND_UNNECESSARY:无需舍位
ROUND_UP与ROUND_DOWN相反,远离0的方向
正数:1.1-> 2   1.5-> 2   1.8-> 2
负数:-1.1-> -2   -1.5-> -2   -1.8-> -2

3.其他方法:

  • BigInteger and(BigInteger val) 返回两个大整数的按位与的结果
  • BigInteger andNot(BigInteger val) 返回两个大整数与非的结果
  • double doubleValue() 返回大整数的double类型的值
  • float floatValue() 返回大整数的float类型的值
  • BigInteger gcd(BigInteger val) 返回大整数的最大公约数
  • int intValue() 返回大整数的整型值
  • long longValue() 返回大整数的long型值
  • BigInteger not() 返回当前大整数的非
  • BigInteger or(BigInteger val) 返回两个大整数的按位或
  • BigInteger xor(BigInteger val) 返回两个大整数的异或
  • BigInteger leftShift(int n) 将当前大整数左移n位后返回
  • BigInteger rightShift(int n) 将当前大整数右移n位后返回
  • byte[] toByteArray(BigInteger val)将大整数转换成二进制反码保存在byte数组中

4.进制转换:

        String string1 = new BigInteger("20", 10).toString(2);
        Log.d("TAG","十进制的20转换成二进制是:"+string1);
 
        String string2 = new BigInteger("20", 10).toString(8);
        Log.d("TAG","十进制的20转换成八进制是:"+string2);
 
        String string3 = new BigInteger("20", 10).toString(16);
        Log.d("TAG","十进制的20转换成十六进制是:"+string3);
 
        String string4 = new BigInteger("110", 2).toString(10);
        Log.d("TAG","二进制的110转换成十进制是:"+string4);
 
        String string5 = new BigInteger("110", 8).toString(10);
        Log.d("TAG","八进制的110转换成十进制是:"+string5);
 
        String string6 = new BigInteger("110", 16).toString(10);
        Log.d("TAG","十六进制的110转换成十进制是:"+string6);

拿例题说话

例题1:

题目描述:

输入两行分别表示两个正整数a,b,求出a除以b的商以及余数。
a的长度不超过1000,b不超过int范围。

输出格式:

输出两行分别表示商和余数

 输入样例:

1388234123
66666

输出样例:

20823
48005

 完整代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;

public class p1091 {
    public static void main(String[] args) {
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        //BufferedReader类从字符输入流中读取文本并缓冲字符,以便有效地读取字符,数组和行
        //可以通过构造函数指定缓冲区大小也可以使用默认大小。对于大多数用途,默认值足够大
        //BufferedReader的读取速度是比Scanner更快的
        try {
            String num1=br.readLine();//读取输入内容
            String num2=br.readLine();
            BigDecimal bd1=new BigDecimal(num1);//这里用的是浮点数
            BigDecimal bd2=new BigDecimal(num2);
            BigDecimal bd3= bd1.divide(bd2,0, RoundingMode.DOWN);//获取商
            System.out.println(bd3);
            System.out.println(bd1.subtract(bd3.multiply(bd2)));//获取余数
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

例题2:

题目描述:

求两个正整数a、b的最小公倍数。

输入格式:

输入有若干行,每行有两个被空格隔开的正整数a、b,数字不超过10^9。

输出格式:

对于每一行,对应输出a、b的最小公倍数。

输入样例:

3 5
6 9
20 16

100000 288

输出样例:

15
18
80

900000

 完整代码:

import java.math.BigInteger;
import java.util.Scanner;

public class p1054 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        String num1,num2;
        BigInteger b1,b2,g;
        while(sc.hasNext()){
            num1=sc.next();
            num2=sc.next();
            b1=new BigInteger(num1);
            b2=new BigInteger(num2);
            g=b1.gcd(b2);//g是b1和b2的最大公约数
            System.out.println(g.multiply(b1.divide(g)).multiply(b2.divide(g)));
            //b1和b2的最小公倍数是b1和b2的最大公约数乘b1和g的商再乘b2和g的商
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_52473454/article/details/122515405
今日推荐