— Diana MacDonald (@didoesdigital) June 20, 2019
Lots of Beyonce gifs ahead
We use numbers for a lot of things, but generally to…
There are many, many different types of number…
- Natural numbers (positive integers)
- Integers (negative and positive whole numbers)
- Rational numbers
- Irrational numbers
- Complex numbers
- Transcendental numbers
- Infinity and negative infinity
- Real numbers – a superset of all possible numbers
- …and many more
We can communicate numbers in many different ways. We can say eight and people may think of
— Klee Thomas (@kleeut) June 20, 2019
We have many notations: base 10, binary, octal, hex, scientific…
This limits the range we can display, although it’s in the quadrillions so generally not a big issue. We also consider precision, JS goes to 17 decimal points.
Everything on computers ends up in binary, which can’t do decimals. Floating point arithmetic has to work around this by encoding numbers. You can break down a 64 bit binary number into its components, the Sign (1 bit), Exponent (11 bits) and Significand precision (53 bits, 52 explicitly stored).
Fractions are tricky. 1/3 is a recurring decimal 0.33333…… so we round it. And you know this is painful when you are trying to create a three-column layout…
How big an issue is this? Depends what the number relates to. The rounding error if you were measuring a light year with 0.00000000000000004ly margin for error, that’s about as big as a bowling pin. In most applications it just isn’t big enough to be an issue; but in something like banking it can become a really big problem very quickly.
Range: size does matter. We make a trade off between range and precision.
If you type
1.7e308 into your JS console, you get
1.7e+308; but if you type in
1.8e308 you get infinity.
We have names for big numbers going up to centillion (a number followed by 303 zeros), and JS goes beyond that. It’s a massive range but we can’t display them all safely.
If you try…
Number.MAX_SAFE_INTEGER + 1
Number.MAX_SAFE_INTEGER + 2
Number.MAX_SAFE_INTEGER + 3
Number.MAX_SAFE_INTEGER + 4
…this will not do what you would expect! You get:
9007199254740991 9007199254740992 9007199254740992 9007199254740994 9007199254740996
Meggan ran into this problem while working on a very large music database at Jaxta. There was an error where the main artist on a listing was coming up as the featured artist. Which wasn’t right, even if Meggan does contend that Double Beyonce isn’t a bug!
The core of the problem was that two artist resources were coming back with the same ID. How was this possible? The actual IDs were returned as different numbers from the back end; but since they were beyond MAX_SAFE_INTEGER, JS was turning them into the same number. Solution was to move IDs to a 128-bit UUID strings.
So, long term what can JS do about this? Enter
BigInt. A new numeric primitive for JS, allowing representation of numbers beyond
MAX_SAFE_INTEGER. This can be expressed with
n or by explicit cast:
We can do arithmetic with BigInts, noting it will still round to whole integers. Can’t do mixed type operations, although we can compare
0n == 0 and sort mixed numbers.
Plus if you try this…
let bigNumber = BigInt(Number.MAX_SAFE_INTEGER)
bigNumber + 1n
bigNumber + 2n
bigNumber + 3n
bigNumber + 4n
bigNumber + 5n
Numbers in JS can be confusing, but when you understand why they were implemented that way it’s easier to figure out the bugs.
Things to look out for:
- we only have 64 bits, so round numbers
- remember MAX_SAFE_INTEGER
(funky electronic music) (audience applauding) – Thank you.
Hi, so great to be here today.
I have actually slightly rejigged the title of my talk from When Your Code Does A Number On You to Double the Beyonce, ’cause it’s more aligned with my passions.
(audience laughing) And there’s gonna be a bit of talk about Beyonce here. Can I get a show of hands, who is a fan of Beyonce? Oh, good, good.
All right, you’re gonna enjoy this.
Everyone else, listen to better music. (laughs) (audience laughing) Cool, so I am Meggan.
So, I hope, you are ready.
There’s gonna be a lot of Beyonce gifs, so I’m not sorry. Let’s go back to basics, what is a number? Well, a number is an abstract concept which we use to count, measure, label and identify things. We pretty much use them every day.
If you’re not, I don’t know what you’re doing. So, they’re pretty ingrained into, like, we understand them. And to help us understand numbers, we can classify them. So, we can group them into different categories. So, we have our natural numbers, which is positive integers, or whole numbers. We have our integers, which is negative and positive whole numbers and include zero. We have rational numbers, which are numbers that can be expressed as fractions.
Irrational numbers are numbers that cannot be expressed as fractions, such as pi or the square root of two.
And we also have real numbers, so real numbers is like a superset of all of the above categories and represents everything that we can see on a number line.
But that’s not it.
We can also have complex numbers, transcendental, imaginary, infinity and negative infinity, infinitesimals, surreal, none of which I know anything about, so we’re not gonna go into that. (chuckles) That’s your homework. (chuckles) And just as we can categorise them in different ways, we can also represent them in different ways. And when I say that, I just mean how we communicate them. So, we can communicate numbers in different ways in human languages.
So, if I asked you all to think of the number eight, a lot of us would think of the Arabic numeral eight. Some of us might think of Ba, which is the Mandarin Chinese character.
Some of us might think Athais, which the Hindi character for the number eight.
So, all of these are the same thing, just slightly different in terms of writing and pronunciation, which I apologise, if I’ve just butchered.
Feel free to come and correct me later.
We have base 10, which is pretty standard, if we think of the number 1,234,567, that’s it in Base 10, we should all kinda understand that. I hope.
We also have binary, which is base two.
Octal, which is base eight.
Hexadecimal, which is base 16.
You’ll definitely be familiar with that, if you’re also writing CSS.
And we also have scientific notation, which is what we’ll use, if we’re representing really large numbers.
They actually have limited space.
They’re pretty basic.
There’s a pun there.
(audience laughing) Thank you. (laughs) And everything in a computer is just binary. It’s all just ones and zeros.
That says hello, Melbourne.
Hi. (chuckles) Yeah, so regardless of how you write your number, whether it’s in base 10, base two, base eight, base 16, it’s always gonna be stored in binary, and it’s always gonna be stored in its binary floating-point representation. We can’t have decimal points in binary.
Decimal, coming from Deka, meaning 10, for base 10. Binary is base two.
So, floating-point arithmetic tries to account for that. Let’s think of that number 1,234,567 again. If I were to represent that in its floating-point form, that would be what it looks like.
A bit of a mouthful, I’m not gonna say it out loud. But let’s break that down.
So, in that number, it’s 64 digits, so 64 bits. And it helps, if you are familiar with scientific notation, it’s gonna kinda help me explain what each of those bits is for.
So, our first bit represents the sign.
So if it’s a zero, it’s a positive number.
If it’s a one, it’s a negative number.
The next 11 bits represent the exponent, which means how far along we’re gonna move that decimal point.
And the next 52 bits represent the significand or mantissa. And that’s the actual, like, digits around the number. That’s that representation.
0.3 and then a bunch of zeros and a four, which we know is incorrect.
When I was in school, one of my teachers always used to tell me that calculators are never wrong, and humans are wrong.
Yeah, all right.
That’s all I have to say about that.
(audience laughing) (Meggan chuckles) So, when we’re representing numbers in binary, integers are totally fine.
They’re super easy to convert, that’s no trouble. It’s when we start representing fractions or more complicated decimals, that we have trouble making that conversion. So, let’s think about 1/3.
If we convert that into decimal, we can’t convert it perfectly.
It’s just a recurring decimal.
So, we say 0.333, and then maybe that’s it. We don’t say 0.33333333333333333333 for the rest of our lives, because that’s silly. We round it.
But to anyone who’s ever tried to create maybe a three-column layout, pre-grid, you’ll know that when you multiply 0.33 times three, you’re not gonna get one.
There’s always gonna be that pesky little 1%. Aaargh, every time.
Super easy to represent.
If I represent that in binary, it’s this much longer number. So, we get 0.000110011001100, and it’s a repeating, it’s a recurring one, one, zero, zero. It happens forever.
But because we only have 64 bits to represent this number, we just have to round it.
So when we convert it back, we get this.
So, that’s where this problem comes from.
Let’s think about how big a problem is this really. Let’s say, I was measuring one centimetre, and I had a margin of error of 0.00000000000000004 centimetres.
That would be about 0.0000000003 times as long as a glucose molecule.
What about a kilometre? If I were trying to measure one kilometre, and I had a margin of error of 0.00000000000000004 kilometres, that’d be 0.0000000000000000000001 times as long as the distance from Earth to the Moon.
Pretty tiny, but still hard to comprehend.
Let’s go big.
If I were trying to measure a light-year, that’s the distance that light travels in a year, it’s a long way.
And if I had one light year, and I had a margin of error of 0.00000000000000004 light-years, that’d be about 38.38 centimetres, which is like this, all about the height of a thin bowling pin. That’s a fun fact. (chuckles) So, it feels like it’s pretty negligible.
And in most cases that degree of accuracy is probably not gonna be that important.
So, moving on.
We’ve talked a bit about precision.
Let’s talk more about that range.
‘Cause size does matter. (chuckles) So, I’ve talked about a trade-off between range and precision, because we can’t have both. We can’t have a huge range and hugely precise numbers. We can make sacrifices in each of those.
So this is scientific notation that we were talking about earlier.
So, that’s a lot bigger than nine quadrillion, nine quadrillion is only like a 16-digit number. This a 310-digit number.
But if I type in 1.8 times 10 to the power of 308, I get infinity? Which isn’t really how I thought maths worked, but I’m not an expert.
We have this number 1.79, all of the other numbers, times 10 to the power of 308, which is a huge number. And we also have a minimum value, which is 0.5, sorry, five times 10 to the power of negative 325, so that’s 0. 324 zeros, five.
If you’re still not convinced that it’s a good range, I want you to think about this number.
3.28 times 10 to the power of 80.
It’s the number of particles in the Universe. So, I don’t know what you would be doing with numbers greater than that.
If you go beyond either of these numbers, things start to get a little bit weird.
Let’s have a look at that maximum safe integer. It ends in 991, just want you to focus on the last three digits, ’cause otherwise it’s gonna get, it’s gonna be hard to read. (chuckles) Let’s add one to that.
Okay, all right, well, I don’t know why that top one is the safe integer, I can see that if I add one, it’s fine. Keep going, oh.
That’s not what I would expect.
It’s not, it’s not really how it works, but the arithmetic just doesn’t add up. (chuckles) It’s just, yeah, it just doesn’t make any sense. And so, this particular issue is what actually inspired this talk.
So, I was at work one day, and for a bit of context, at Jaxsta we’re building a massive database of music credits, so you can go on and look up your favourite song and see who produced it, mixed it, whatever. And I was sitting at work one day, and I was rendering data on a page, which is like 50% of my job, so I’d like to think I’m okay at it.
And I noticed this strange bug.
Let’s just pretend that I was looking at Beyonce’s “Drunk in Love” page.
Happens to be one of my favourite karaoke songs, if anyone’s interested later.
Also, in case you weren’t aware and up to date with Beyonce’s discography, this album cover is where my T-shirt comes from, BeyonCSS, Beyonce, ah. (Meggan laughs) And it also happens to have actually been designed by Henri, who was just on before me, so amazing coincidence. (chuckles) Yeah.
Right, so I was at work, and I was looking at this page, and I noticed an issue.
I had Beyonce appearing twice on my page, where she shouldn’t have.
So, she was coming up as the main artist on “Drunk in Love”, but she was also coming up as the featured artist, which is a bit weird to be both the main and the featured artist, especially because she is not the featured artist on this song, Jay-Z is the featured artist on this song.
And so, Beyonce is not happy about this.
She would like Jay to be represented too.
And so, I had a look inside of my developer tools, and I noticed something funny.
So, I had two resource subjects coming through, One for Beyonce and one for Jay-Z, but their IDs were the same, which is weird, because IDs are supposed to be unique.
And I had a look at that number, and I was scratching my head for about three hours. Just like, what? Looking in the database, seeing this, they’re not even the same IDs.
Yeah, hence this problem, where I had no Jay-Z and just Beyonce on the page. The front-end framework that I’m using is Ember, and the data layer on that said, “Hey, I’ve already got “a resource object with that ID, “so I’m not gonna fetch it again.
“So just have double Beyonce.” Which to me is the feature more than a bug. (audience laughing) My boss didn’t agree, so I had to fix it. (chuckles) And now, solution here was to move out IDs to UIDs. So, UIDs are 128-bit number, but because they contain non-numeric characters, they have to come through as strings.
Yeah, so, we moved to UIDs, and now Beyonce and Jay-Z are both coming through with different IDs, which is correct.
And now they’re both on the page, which is great. (chuckles) And the king and queen are happy. (chuckles) Yeah, so, big numbers are kinda weird, but this just in, is a new numeric primitive called BigInt. Has anyone heard of BigInt? Okay, a few people, cool.
So this is really exciting, or it was really exciting to me when I was putting together this talk, yeah. So, BigInt is a new numeric primitive, which was just introduced last year.
It’s still really new.
And it’s only available in Chrome, Firefox 68 beta and Edge.
But it basically means that we can represent numbers beyond that maximum safe integer.
So, let’s have a look at how it’s implemented. If we wanna create a BigInt, we can just append an N to a number, and we’ll get that green type, is now BigInt. Or we can also use the BigInt like method in passing a number.
We cannot do this with any decimals or floats, because it’s a BigInt, integer, whole number. So, we’re not gaining any precision here, unfortunately, but this could kind of set the path for maybe a BigDecimal type in the future.
So, we can do arithmetic with other big integers, so we can add 100n to 100n and get 200n.
Or we can subtract.
The behaviour is expected.
We can multiply.
We can divide, but because we can only return whole integers, it will just always round to zero, so 10 divided by three is 3.33333, but we just get three. We can do the to the power of operator.
And we can also use modular.
We can’t do any mix type operations, so we can’t mix big integers and regular integers. But we can compare.
Big integers have loose equality to numeric types, and we can also compare them like normal.
So, 1n is not bigger than two.
And because we can compare them as normal, we can also sort them, if there’s like a mixed array for some reason.
So, let’s have a look at the maths of this properly. We had this problem earlier, where, when we were adding beyond that maximum safe integer, things got a little bit weird.
Let’s do this with our new BigInt.
So, I’ll set a variable of bigNum, and I’ll add one. Okay, it’s working.
I add two, it’s working.
Oh my god, it just keeps working.
(audience laughing) It’s so beautiful. (chuckles) So, it works, which is really amazing.
I was excited about this.
You don’t have to be, but that’s all right. (chuckles) So, some caveats, of course.
It’s not ready for production apps yet, because it’s only available in a few different browsers. And there’s not a lot of documentation available, so it is a fairly new feature, less than a year old. It was only shipped in Firefox like two weeks ago too, so that’s, yeah.
I still think it’s like this.
This is how I feel about BigInt.
Some babies grow up to be Beyonce.
So, you know, it’s pretty cool.
And Dan Abromov kind of summed this up.
And so, there’s gonna be precision errors.
So, if you are dealing with precise numbers, round them. You don’t need to know your shopping cart to 17 decimal places, you only really need to know at two. And when you’re dealing with larger numbers, just remember that there is a maximum safe integer, and that BigInt is coming, which is cool.
(Meggan chuckles) (audience laughing) Or a little less just like confused Beyonce. (audience laughing) And mostly Beyonce. (chuckles) Thank you so much. (chuckles) (audience applauding) (funky electronic music)