-
Notifications
You must be signed in to change notification settings - Fork 68
/
chapter04-numbers.jsh
115 lines (87 loc) · 4.16 KB
/
chapter04-numbers.jsh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// To starts, run jshell --enable-preview which is a program able to interpret Java syntax
// then cut and paste the following lines to see how it works
// To exit jshell type /exit
// # Numbers
// This chapter in not specific to Java but more on how integers and floating point numbers
// work on CPUs like Intel 64-bit or ARM 64-bit.
// ## Integers
// ### Dividing by zero
// If you try to divide by 0, you get an exception
System.out.println(1 / 0);
System.out.println(1 % 0); // remainder of the division
// ### Overflow
// Given that integers are represented using a fixed number of bits,
// there is a minimum/maximum number that can be represented
System.out.println("max " + Integer.MAX_VALUE);
System.out.println("min " + Integer.MIN_VALUE);
// integers can overflow, if a positive integers is too big, it becomes negative
System.out.println(Integer.MAX_VALUE + 1);
// and vice versa
System.out.println(Integer.MIN_VALUE - 1);
// In Java, you have safe alternatives that are slower but throw an exception
// if the computation overflow
Math.addExact(Integer.MAX_VALUE, 1);
Math.subtractExact(Integer.MIN_VALUE, 1);
// You can notice that the minimum value is one less than the maximum value
// in absolute value so `Math.abs()` has an overflow issue
// because -Integer.MIN_VALUE is not Integer.MAX_VALUE
System.out.println(Math.abs(Integer.MIN_VALUE));
// When trying to find the middle between two values, the calculation may also overflow
// so the result becomes nagative
int middle(int value1, int value2) {
return (value1 + value2) / 2;
}
System.out.println(middle(Integer.MAX_VALUE, 1));
// In this specific case, you can recover from the overflow by using
// the triple shift operator `>>>` that doesn't consider the sign bit as a sign bit
// but as a bit which is part of the value
int middle(int value1, int value2) {
return (value1 + value2) >>> 1;
}
System.out.println(middle(Integer.MAX_VALUE, 1));
// ## Double precision floating point numbers
// The computation using floating point in not precise because not all values are
// directly representable so the CPU will use the closest value.
// It's like using 3.14 when you ask for π
// So when you do a computation, the error propagates and becomes visible
System.out.println(0.1 + 0.2);
// When you print a double, there is a trick, only some decimals will be printed
// so you may think the value is fully represented that but that's just an illusion
System.out.println(1.0 / 3.0);
// On way to see the trick is to ask a float (32-bit), to be printed as a double (64-bit).
System.out.println(1.0f / 3.0f);
System.out.println(Float.toString(1.0f / 3.0f));
System.out.println(Double.toString(1.0f / 3.0f)); // damn i'm unmasked
// ### No exception
// The computation is said `secured` so instead of having an exception thrown
// when you divide by 0, you have three special values of double to represent the result
// of the computation
// +Infinity
System.out.println(1.0 / 0.0);
System.out.println(Double.POSITIVE_INFINITY);
// -Infinity
System.out.println(-1.0 / 0.0);
System.out.println(Double.NEGATIVE_INFINITY);
// Not A Number
System.out.println(0.0 / 0.0);
System.out.println(Double.NaN);
// ### NaN
// Not a Number is very weird, because by definition, it's the number which is not equal to itself
// Don't use == to test NaN, it will not work
System.out.println(Double.NaN == Double.NaN);
// The only way to test is NaN is NaN is to test if it is equals to itself (by definition)
boolean isNotANumber(double x) {
return x != x;
}
System.out.println(isNotANumber(Double.NaN));
// An equivalent static method to isNotANumber already exist in Double, `Double.isNaN()`
System.out.println(Double.isNaN(Double.NaN));
// ### Record and NaN
// To avoid the issue of a record r not equals to itself because it has a component
// that contains NaN the implementation of `equals()` for a record checks
// the raw bytes of the double after all NaN (yes internally there are several possible
// representation of NaN) are collapsed into one so testing if two records are equals works as expected !
record MagicBeer(double content) { }
var beer1 = new MagicBeer(Double.NaN);
var beer2 = new MagicBeer(Double.NaN);
System.out.println(beer1.equals(beer2));