Skip to content

Commit 799dd69

Browse files
committed
真随机数
1 parent 984b585 commit 799dd69

3 files changed

Lines changed: 124 additions & 3 deletions

File tree

src/com/yale/test/math/BigDecimalTest.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@
4444
* 就是127,如果在double11位的阶码中偏移值就是1023,在计算时要注意这一点.
4545
* 阶码:用移码(标准移码-1)记录指数,实际偏移值为(2e-1 - 1).
4646
*
47-
* 使用BigDecimal进行浮点精确计算
47+
* 使用BigDecimal进行浮点精确计算,BigDecimal用于表示精确的小数,常用于财务计算;
48+
* 和BigInteger类似,BigDecimal可以表示一个任意大小且精度完全准确的浮点数。
49+
* 如果查看BigDecimal的源码,可以发现,实际上一个BigDecimal是通过一个BigInteger和一个scale来表示的,即BigInteger表示一个完整的整数,而scale表示小数位数:
4850
* @author dell
4951
*/
5052
public class BigDecimalTest {
@@ -88,5 +90,38 @@ public static void main(String[] args) {
8890
System.out.println("有些人可能会有疑问既然BigDecimal可以精确表示小数,那为啥要用Double这种不精确的类型呢?实际上现实生活中很多数值本来就不是很精确的");
8991
System.out.println("就像一部电影的时长你可以精确到分秒,但是你没必要精确到毫秒或者纳秒了,大部分情况下我们只需要有限范围内的精确就可以了,");
9092
System.out.println("而且基本类型的存储和计算效率会高很多,使用他是一个取舍的结果");
93+
94+
BigDecimal d1 = new BigDecimal("123.45");
95+
System.out.println("BigDecimal用scale()表示小数位数,例如:" + d1.scale());
96+
//通过BigDecimal的stripTrailingZeros()方法,可以将一个BigDecimal格式化为一个相等的,但去掉了末尾0的BigDecimal:
97+
BigDecimal d11 = new BigDecimal("123.4500");
98+
BigDecimal d2 = d1.stripTrailingZeros();
99+
System.out.println(d11.scale()); // 4
100+
System.out.println(d2.scale()); // 2,因为去掉了00
101+
102+
103+
BigDecimal d3 = new BigDecimal("1234500");
104+
BigDecimal d4 = d3.stripTrailingZeros();
105+
System.out.println(d3.scale()); // 0
106+
System.out.println(d4.scale()); // -2
107+
System.out.println("如果一个BigDecimal的scale()返回负数,例如,-2,表示这个数是个整数,并且末尾有2个0。");
108+
109+
//调用divideAndRemainder()方法时,返回的数组包含两个BigDecimal,分别是商和余数,其中商总是整数,余数不会大于除数。我们可以利用这个方法判断两个BigDecimal是否是整数倍数:
110+
BigDecimal n = new BigDecimal("12.75");
111+
BigDecimal m = new BigDecimal("0.15");
112+
BigDecimal[] dr = n.divideAndRemainder(m);
113+
if (dr[1].signum() == 0) {
114+
// n是m的整数倍
115+
System.out.println("n是m的整数倍");
116+
} else {
117+
System.out.println("n不是m的整数倍");
118+
}
119+
120+
//在比较两个BigDecimal的值是否相等时,要特别注意,使用equals()方法不但要求两个BigDecimal的值相等,还要求它们的scale()相等:
121+
BigDecimal d111 = new BigDecimal("123.456");
122+
BigDecimal d22 = new BigDecimal("123.45600");
123+
System.out.println(d111.equals(d22)); // false,因为scale不同
124+
System.out.println(d111.equals(d22.stripTrailingZeros())); // true,因为d2去除尾部0后scale变为2
125+
System.out.println("推荐 总是使用compareTo()比较两个BigDecimal的值,不要使用equals()! " + d111.compareTo(d22)); // 0
91126
}
92127
}

src/com/yale/test/math/BigIntegerTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
* volatile解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。
1111
说明:如果是count++操作,使用如下类实现:AtomicInteger count = new AtomicInteger(); count.addAndGet(1);
1212
如果是JDK8,推荐使用LongAdder对象,比AtomicLong性能更好(减少乐观锁的重试次数)。《阿里巴巴Java开发手册(泰山版).
13+
在Java中,由CPU原生提供的整型最大范围是64位long型整数。使用long型整数可以直接通过CPU指令进行计算,速度非常快。
14+
和long型整数运算比,BigInteger不会有范围限制,但缺点是速度比较慢。
1315
* @author dell
1416
*/
1517
public class BigIntegerTest {
@@ -48,6 +50,29 @@ public static void main(String[] args) {
4850
int res = b1.compareTo(b2);
4951
System.out.println("b1和b2比较,返回-1(小于) 0(等于) 1(大于):" + res);
5052

53+
BigInteger bi = new BigInteger("1234567890");
54+
System.out.println("pow:" + bi.pow(5)); // 2867971860299718107233761438093672048294900000
55+
System.out.println("也可以把BigInteger转换成long型:" + bi.longValue());
56+
//使用longValueExact()方法时,如果超出了long型的范围,会抛出ArithmeticException。
57+
System.out.println("也可以把BigInteger转换成long型:" + bi.longValueExact());
58+
/*
59+
* BigInteger和Integer、Long一样,也是不可变类,并且也继承自Number类。因为Number定义了转换为基本类型的几个方法:
60+
转换为byte:byteValue()
61+
转换为short:shortValue()
62+
转换为int:intValue()
63+
转换为long:longValue()
64+
转换为float:floatValue()
65+
转换为double:doubleValue()
66+
因此,通过上述方法,可以把BigInteger转换成基本类型。如果BigInteger表示的范围超过了基本类型的范围,转换时将丢失高位信息,即结果不一定是准确的。
67+
如果需要准确地转换成基本类型,可以使用intValueExact()、longValueExact()等方法,在转换时如果超出范围,将直接抛出ArithmeticException异常。
68+
*/
69+
70+
BigInteger n = new BigInteger("999999").pow(99);
71+
float f = n.floatValue();
72+
System.out.println("如果BigInteger的值甚至超过了float的最大范围(3.4x1038),那么返回的float是什么呢?" + f);
73+
System.out.println("如果BigInteger的值甚至超过了float的最大范围(3.4x1038),那么返回的float是什么呢?" + Float.isInfinite(f));
74+
75+
5176
BigInteger big01 = new BigInteger("100");
5277
System.out.println("BigInteger的&按位与运算:" + big01.and(new BigInteger("1")));
5378
System.out.println("BigInteger的|按位或运算:" + big01.or(new BigInteger("1")));

src/com/yale/test/math/MathTest.java

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package com.yale.test.math;
22

33
import java.math.BigDecimal;
4+
import java.security.NoSuchAlgorithmException;
5+
import java.security.SecureRandom;
6+
import java.util.Arrays;
47
import java.util.Random;
58
/**
69
* java.lang.Math(jdk1.0开始有的)类是整个JDK里面唯一一个与数学计算有关的程序类。这里面提供有一些基础的数学函数。
@@ -20,6 +23,22 @@ public static void main(String[] args) {
2023
System.out.println("Math.pow计算某个数的平方:" + Math.pow(10, 3));//10的3次方是1000
2124

2225
System.out.println("max方法返回俩个数字最大的那一个:" + Math.max(18.44, 55));
26+
System.out.println("min方法返回俩个数字最小的那一个:" + Math.min(18.44, 55));
27+
28+
System.out.println("求绝对值:" + Math.abs(-7.8));
29+
System.out.println("计算√x:" + Math.sqrt(2));
30+
System.out.println("计算ex次方:" + Math.exp(2));
31+
System.out.println("计算以e为底的对数:" + Math.log(2));
32+
System.out.println("计算以10为底的对数:" + Math.log10(2));
33+
/*
34+
* 三角函数:
35+
* Math.sin(3.14); // 0.00159...
36+
Math.cos(3.14); // -0.9999...
37+
Math.tan(3.14); // -0.0015...
38+
Math.asin(1.0); // 1.57079...
39+
Math.acos(1.0); // 0.0
40+
41+
*/
2342

2443
System.out.println(Math.round(18.44));
2544
System.out.println(Math.round(18.49));
@@ -34,19 +53,61 @@ public static void main(String[] args) {
3453
System.out.println(MathTest.myRoundSec(-18.555, 2));//这个方法负数依然不能正确得到四舍五入的值,这行代码的结果为-18.55
3554
System.out.println(MathTest.myRoundThi(-18.555, 2));//这个方法负数依然不能正确得到四舍五入的值,这行代码的结果为-18.55
3655

37-
System.out.println("Random是随机数类");
56+
System.out.println("Random是随机数类,Random用来创建伪随机数。所谓伪随机数,是指只要给定一个初始的种子,产生的随机数序列是完全一样的。");
57+
System.out.println("有童鞋问,每次运行程序,生成的随机数都是不同的,没看出伪随机数的特性来。");
58+
System.out.println("这是因为我们创建Random实例时,如果不给定种子,就使用系统当前时间戳作为种子,因此每次运行时,种子不同,得到的伪随机数序列就不同。");
3859
Random dom = new Random();
3960
for (int x=0;x<10; x++) {
4061
System.out.print(dom.nextInt(100) + ",");//100是上限,输出的随机最大值是99
4162
}
4263

64+
//如果我们在创建Random实例时指定一个种子,就会得到完全确定的随机数序列:
65+
Random rd = new Random(12345);
66+
for (int i = 0; i < 10; i++) {
67+
System.out.println("Main方法每次运行随机序列是完全一样的:" + rd.nextInt(100));
68+
}
69+
/*
70+
* 有伪随机数,就有真随机数。实际上真正的真随机数只能通过量子力学原理来获取,而我们想要的是一个不可预测的安全的随机数,SecureRandom就是用来创建安全的随机数的:
71+
* SecureRandom无法指定种子,它使用RNG(random number generator)算法。JDK的SecureRandom实际上有多种不同的底层实现,
72+
* 有的使用安全随机种子加上伪随机数算法来产生安全的随机数,
73+
* 有的使用真正的随机数生成器。实际使用的时候,可以优先获取高强度的安全随机数生成器,如果没有提供,再使用普通等级的安全随机数生成器:
74+
* SecureRandom的安全性是通过操作系统提供的安全的随机种子来生成随机数。这个种子是通过CPU的热噪声、读写磁盘的字节、网络流量等各种随机事件产生的“熵”。
75+
* 在密码学中,安全的随机数非常重要。如果使用不安全的伪随机数,所有加密体系都将被攻破。因此,时刻牢记必须使用SecureRandom来产生安全的随机数。
76+
* 需要使用安全随机数的时候,必须使用SecureRandom,绝不能使用Random!
77+
*/
78+
SecureRandom sr = null;
79+
try {
80+
sr = SecureRandom.getInstanceStrong(); // 获取高强度安全随机数生成器
81+
} catch (NoSuchAlgorithmException e) {
82+
sr = new SecureRandom(); // 获取普通的安全随机数生成器
83+
}
84+
byte[] buffer = new byte[16];
85+
sr.nextBytes(buffer); //用安全随机数填充buffer
86+
System.out.println("真随机数:" + Arrays.toString(buffer));
87+
System.out.println("真随机数:" + sr.nextInt(100));
88+
4389
/**
4490
* Math.random()方法用的也是random生成随机数的,值范围是0-1,随机最大小数位0.9999999999
4591
* 【强制】注意 Math.random() 这个方法返回是double类型,注意取值的范围 0≤x<1(能够取到零值,注意除零异常),
4692
* 如果想获取整数类型的随机数,不要将x放大10的若干倍然后取整,直接使用Random对象的nextInt或者nextLong方法。
4793
* 《阿里巴巴Java开发手册(泰山版).pdf》
94+
* 前面我们使用的Math.random()实际上内部调用了Random类,所以它也是伪随机数,只是我们无法指定种子。
4895
*/
49-
System.out.print(Math.random() + ",");//100是上限,输出的随机最大值是99
96+
System.out.print("x的范围是0 <= x < 1:" + Math.random() + ",");//100是上限,输出的随机最大值是99
97+
//如果我们要生成一个区间在[MIN, MAX)的随机数,可以借助Math.random()实现,计算如下:
98+
double xrd = Math.random(); // x的范围是[0,1)
99+
double min = 10;
100+
double max = 50;
101+
double yd = xrd * (max - min) + min; // y的范围是[10,50)
102+
long nl = (long) yd; // n的范围是[10,50)的整数
103+
System.out.println(yd);
104+
System.out.println(nl);
105+
106+
/*
107+
* 有些童鞋可能注意到Java标准库还提供了一个StrictMath,它提供了和Math几乎一模一样的方法。这两个类的区别在于,由于浮点数计算存在误差,不同的平台(例如x86和ARM)计算的结果可能不一致(指误差不同),
108+
* 因此,StrictMath保证所有平台计算结果都是完全相同的,而Math会尽量针对平台优化计算速度,所以,绝大多数情况下,使用Math就足够了。
109+
*/
110+
double sm = StrictMath.random();
50111

51112
System.out.println();
52113

0 commit comments

Comments
 (0)