Talks

1.5 is the Midpoint Between 0 and Infinity

What’s the midpoint between 0 and infinity? Well, the answer differs depending on whether you are asking a mathematician, philosopher, or a Ruby developer. I’m not a mathematician or a philosopher, but I am a Ruby developer, so I can tell you that 1.5 is the midpoint between 0 and infinity. In this talk, we'll discuss the binary search algorithm, IEEE 754 floating-point numbers, and a clever trick Ruby uses to perform binary search on floating-point ranges.

RubyConf 2022

00:00:00.000 ready for takeoff
00:00:18.539 hello rubyconf my name is Peter I'm on
00:00:21.960 the Ruby core team and I'm a senior
00:00:23.699 developer at Shopify based out of Ottawa
00:00:26.400 Canada
00:00:27.900 today I'll be talking about why 1.5 is
00:00:30.599 the midpoint between 0 and infinity
00:00:33.360 so what do I mean when I say that 1.5 is
00:00:36.420 the midpoint between 0 and infinity
00:00:39.899 it all started when a co-worker asked
00:00:42.300 this question on Slack
00:00:44.100 why is 1.5 the first number Ruby's
00:00:46.980 binary search inspects when searching
00:00:49.379 between 0 and infinity and attach this
00:00:52.440 code snippet
00:00:54.300 the question and the code were both a
00:00:56.160 bit odd to be because how can you search
00:00:58.500 over an infinite range and how is 1.5
00:01:01.559 related to this in any way
00:01:04.260 well let's first take a look at this
00:01:06.299 short Ruby code
00:01:08.280 first creating a range between 0 and
00:01:10.560 infinity
00:01:11.700 it's then calling the range B search
00:01:14.700 method over that range
00:01:17.220 we then output every number that we
00:01:19.439 inspect
00:01:20.580 and then the block returns a Boolean
00:01:22.680 whether the floating Point number passed
00:01:24.659 in is greater than 42 or not
00:01:27.299 essentially we're looking for the First
00:01:29.400 floating Point number that is strictly
00:01:31.439 greater than 42.
00:01:33.840 now let's run this script and seal the
00:01:36.540 output
00:01:41.400 we see a lot of numbers that it inspects
00:01:43.740 with the first number being 1.5 followed
00:01:47.280 by a lot of very large numbers that are
00:01:49.619 seemingly random
00:01:51.119 and then we see the numbers converging
00:01:52.860 closer and closer to 42.
00:01:55.500 so how does range B search figure out
00:01:57.479 what numbers to inspect here
00:01:59.820 in order to understand this output we
00:02:02.159 first have to understand how binary
00:02:03.720 search works and then how floating Point
00:02:05.880 numbers work
00:02:07.979 but before we talk about how binary
00:02:10.140 search works let's talk about how linear
00:02:12.420 search works
00:02:13.620 it's really simple we simply just
00:02:16.140 inspect every element in the list and
00:02:18.840 compare whether or not it's equal to the
00:02:20.520 element we're looking for
00:02:22.200 let's look at an example let's see what
00:02:24.540 let's say we're trying to find the
00:02:25.739 number 15 from this array of integers
00:02:28.620 with linear search we simply scan from
00:02:31.020 the start of the array and check if each
00:02:32.459 element is equal to 15 the elements
00:02:34.500 we're searching for
00:02:35.640 until we find the integer 15 in our
00:02:38.220 array simple right
00:02:40.980 now let's see how binary search works
00:02:43.019 however before we can do that we need a
00:02:46.080 restriction on the list that we're
00:02:47.760 searching over
00:02:48.900 it has to be sorted in either ascending
00:02:51.120 or descending order
00:02:53.040 let's see an example using the same
00:02:54.900 array of integers but sorted in
00:02:56.819 ascending order
00:02:58.379 again we'll try to find the integer 15
00:03:00.540 from this list
00:03:02.220 to use binary search we need to have two
00:03:04.440 cursors a low cursor that points to the
00:03:07.620 first element of the array
00:03:09.900 and a high cursor that points to the
00:03:12.180 last element of the array
00:03:14.040 the two cursors Define the window which
00:03:16.080 we're searching over
00:03:17.519 using these two cursors we'll calculate
00:03:20.519 a third cursor called the mid cursor
00:03:22.620 that points to the element at the
00:03:24.360 midpoint
00:03:25.560 we compared the value of the elements at
00:03:27.540 the midpoint to 15. the elements the
00:03:29.760 number that we're searching for
00:03:31.379 since 48 is greater than 15 we move the
00:03:34.620 high cursor to the mid cursor
00:03:37.440 in a single comparison we just halved
00:03:40.019 the number of elements we're searching
00:03:41.580 over
00:03:42.420 because we know that 15 must be in the
00:03:44.940 window between the low and the high
00:03:46.739 cursors
00:03:48.360 and we repeat this process again
00:03:50.580 using the low and the high cursors will
00:03:52.680 calculate a mid cursor at the index
00:03:54.540 halfway between them which is at integer
00:03:56.819 12. we'll compare 12 and 15 since 12 is
00:03:59.879 less than 15 we'll move the low cursor
00:04:02.519 to the mid cursor
00:04:04.500 once again this comparison half the
00:04:06.720 number of elements we're searching over
00:04:09.299 and we'll repeat this process once again
00:04:11.519 using the low and the high cursors will
00:04:13.560 calculate a mid cursor at the integer 15
00:04:16.079 compare 15 and 15 since they're equal we
00:04:19.440 found the elements we're searching for
00:04:22.079 in the time it took me to explain binary
00:04:24.000 search I could have probably explained
00:04:25.979 linear search five times it's so
00:04:28.080 complicated so why do we use it
00:04:31.320 because if the list is sorted then
00:04:33.720 binary search is much faster especially
00:04:36.479 when we're searching over a large number
00:04:38.220 of elements
00:04:39.479 take a look at this graph the horizontal
00:04:42.120 x-axis shows the number of elements
00:04:44.340 we're searching over
00:04:45.780 and the vertical y-axis shows the number
00:04:48.419 of comparisons we have to do in the
00:04:50.220 worst case
00:04:51.300 for the compare for the computer
00:04:52.560 scientists here you might have heard of
00:04:54.419 The Big O complexity and that's what
00:04:56.340 we're graphing here
00:04:58.259 for linear search we have to do
00:04:59.880 comparisons equal to the number of
00:05:01.620 elements we're searching over in the
00:05:03.300 worst case
00:05:04.380 and this is graphed here as the red line
00:05:06.960 for binary search since every comparison
00:05:09.540 has the number of elements we consider
00:05:12.479 this results in comparisons equal to the
00:05:15.360 logarithm of the base two of the number
00:05:17.580 of elements in the worst case this is
00:05:19.919 graphed here as the blue line
00:05:21.720 we can see that the blue line is
00:05:23.580 significantly lower than the red line
00:05:25.560 for example for an array of 100 elements
00:05:28.560 then your search would have to do at
00:05:30.539 most 100 comparisons whereas binary
00:05:32.580 search would only need to do at most
00:05:34.259 seven comparisons
00:05:37.199 we just looked at how to perform binary
00:05:39.180 search over a sorted array which has
00:05:41.639 finite size and definite endpoints
00:05:44.699 so now you might be asking how can we
00:05:46.919 perform binary search over floating
00:05:48.780 Point numbers there seems to be an
00:05:50.639 infinite number of floating Point
00:05:51.780 numbers and on top of that we're binary
00:05:54.360 searching between 0 and infinity so how
00:05:56.940 is infinity a finite endpoint
00:06:00.600 in order to understand how we can binary
00:06:02.880 search in a range of between 0 and
00:06:05.039 infinity we first have to understand how
00:06:07.620 floating Point numbers are represented
00:06:09.419 internally
00:06:10.500 in particular I'll be talking about how
00:06:12.900 IEEE 754 floating Point numbers are
00:06:15.780 represented
00:06:16.860 all modern CPU architectures we use
00:06:19.020 nowadays such as x86 arm and risk 5 all
00:06:23.580 use IEEE 754 floating Point numbers
00:06:25.979 internally
00:06:27.240 so what I'm about to show you applies to
00:06:29.100 all modern computers
00:06:31.319 for Simplicity I'll be talking about
00:06:33.120 32-bit floating Point numbers also known
00:06:35.520 as single Precision floating Point
00:06:37.319 numbers
00:06:38.220 however Ruby uses 64-bit floating Point
00:06:40.620 numbers internally also known as double
00:06:42.600 Precision floating Point numbers
00:06:44.520 they both work in the same way it's just
00:06:46.740 that 64 bits will be to represent more
00:06:48.960 Precision than 32 bits
00:06:51.720 there are three parts to an IEEE 754
00:06:54.660 floating Point number
00:06:57.360 so one way to explain how these three
00:06:59.160 parts work would be to throw a formula
00:07:00.780 like this at you however I find this to
00:07:03.000 be unintuitive and difficult to
00:07:04.680 understand
00:07:05.819 so I'll move this off to the side and
00:07:07.740 I'll try to explain this using a
00:07:09.180 different approach
00:07:11.699 so first we have the sign
00:07:14.400 this one's pretty self-explanatory it's
00:07:16.800 a single bit that's zero when the number
00:07:18.780 is positive and one when it's negative
00:07:22.380 it's Then followed by the exponent
00:07:25.560 the exponent is 8 Bits in size meaning
00:07:28.020 that the value can range between 0 and
00:07:30.240 255.
00:07:31.979 intuitively the exponent tells us the
00:07:34.259 range which this floating Point number
00:07:35.880 falls into
00:07:37.139 ranges start at the power of 2 and end
00:07:39.539 at the next Power of Two
00:07:41.880 compared to using something like say
00:07:43.620 linear ranges using powers of 2 will let
00:07:46.560 us represent both very large and very
00:07:48.660 small numbers in magnitude
00:07:51.300 this table shows some of the exponent
00:07:53.460 values in decimal on the left and the
00:07:55.680 ranger corresponds to on the right
00:07:58.020 the square bracket on the left hand side
00:08:00.240 of the range means that the starting
00:08:02.039 point is inclusive and the round bracket
00:08:04.680 on the right hand side of the range
00:08:06.060 means that the end point is exclusive
00:08:09.479 ranges start at 2 to the power of the
00:08:11.580 exponent minus 127.
00:08:14.580 and ends at the next power of two we
00:08:17.039 subtract 127 so we are able to have
00:08:19.620 ranges to represent numbers with
00:08:21.180 magnitudes between 0 and 1.
00:08:24.060 for example an exponent with value 125
00:08:27.419 starts at 2 to the power of negative 2
00:08:29.220 which is 0.25 and ends at 2 to the power
00:08:32.279 of negative 1 which is 0.5
00:08:36.120 now that we know the range that the
00:08:37.979 floating Point number is in from the
00:08:39.659 exponent part the significant tells us
00:08:42.719 where the number is in the range
00:08:45.660 using 23 bits the significant can range
00:08:48.480 between 0 and 2 to the power of 23 minus
00:08:51.660 1.
00:08:52.800 this divides the range into 2 to the
00:08:54.720 power of 23 parts
00:08:56.459 which is about 8.4 Million Parts
00:09:00.720 the significant tells us how far into
00:09:03.180 the range the floating Point number lies
00:09:05.760 and the significance splits the range
00:09:07.920 into 8.4 million even parts
00:09:11.040 it's interesting to note that because
00:09:13.019 the significant can only divide the
00:09:15.000 range into a finite number of parts
00:09:17.580 it means that certain numbers cannot be
00:09:19.560 precisely represented as floating Point
00:09:21.959 numbers
00:09:23.160 you can actually see the consequences of
00:09:25.320 this
00:09:26.279 try opening up IRB or Ruby script and
00:09:28.920 try to add 0.1 and 0.2
00:09:31.560 you get into a result that's not quite
00:09:33.480 0.3
00:09:35.100 why this is is left as an exercise for
00:09:37.740 you to think about after this talk
00:09:40.920 let's see an example of a floating Point
00:09:43.019 number
00:09:44.220 let's try converting this floating Point
00:09:46.140 number which is in binary into a decimal
00:09:48.720 number
00:09:49.920 we see that the sign is zero meaning
00:09:51.720 that this is a positive number
00:09:54.060 now let's convert the exponent into a
00:09:55.860 decimal number it's the number at 130.
00:09:59.760 this corresponds to the range that
00:10:01.560 starts at 2 to the power over 3 which is
00:10:03.420 8 and enter 2 to the power 4 which is
00:10:05.519 16.
00:10:07.260 the significant corresponds to this
00:10:09.180 decimal number which is about 2.6
00:10:11.339 million
00:10:12.480 to get how far into the range between 8
00:10:14.760 and 16 this floating Point number is in
00:10:17.399 we divide this number by the 2 to the
00:10:19.440 power of 23 number which is this 8.38
00:10:22.560 Million number
00:10:24.060 and we get
00:10:25.940 0.3125 this means that we are 31.25 of
00:10:30.779 the range into eight between the range 8
00:10:33.660 and 16. and we can calculate this to be
00:10:36.420 the number 10.5
00:10:38.160 so these bits represent the number 10.5
00:10:40.560 in floating point
00:10:43.620 now that you've seen how binary search
00:10:45.600 works and how IEEE 754 floating Point
00:10:48.959 numbers work let's see how these two
00:10:51.240 concepts tie together
00:10:53.880 but before we can tie binary search and
00:10:55.920 floating Point numbers together let's
00:10:57.839 look at how the special floating Point
00:10:59.220 number is 0 and infinity are represented
00:11:02.579 for xero it's simply represented by all
00:11:05.160 of the bits being zero
00:11:07.500 for Infinity it's represented by the
00:11:11.040 exponent being all ones and the
00:11:12.839 significant being all zeros
00:11:15.120 note that this is the largest possible
00:11:17.399 floating Point number since IEEE 754
00:11:20.760 uses an exponent of all wines and a
00:11:23.160 non-zero significant to denote the value
00:11:25.320 not a number
00:11:26.880 this value is used to signal that this
00:11:29.700 floating Point number is invalid
00:11:32.579 all right let's go back to these two
00:11:34.140 numbers
00:11:35.100 and let's try to do something that's
00:11:36.839 perhaps a little strange
00:11:38.880 what if we read these floating Point
00:11:40.800 numbers as if they were 32-bit integers
00:11:44.279 for the floating Point number zero it'd
00:11:46.560 Simply Be zero as an integer
00:11:49.019 for the floating Point number infinity
00:11:50.660 it'll be this number that's about 2.1
00:11:53.399 billion
00:11:54.480 again it's important to note that what
00:11:56.880 we're doing here is we're directly
00:11:58.680 reading the bits of the floating Point
00:12:00.360 number as if it was an integer
00:12:03.000 and this is different than casting a
00:12:05.279 floating Point number to an integer
00:12:07.440 we cannot cast a floating Point numbers
00:12:09.839 Infinity into an integer since integers
00:12:12.720 do not have the concept of infinity
00:12:15.240 and Ruby if you try to convert Infinity
00:12:18.120 into an integer an error will be raised
00:12:21.540 in the C language however it will result
00:12:24.120 in undefined Behavior and the result
00:12:26.279 will depend on the system
00:12:28.920 all right let's make some space
00:12:31.019 and let's try to calculate the integer
00:12:33.000 that is at the midpoint between 0 and
00:12:35.459 2.1 billion
00:12:37.440 it's this number that is 1.06 billion in
00:12:40.680 decimal
00:12:41.700 we got this number by adding 0 and this
00:12:44.279 2.1 billion number and then dividing the
00:12:46.740 sum by two
00:12:48.779 this integer looks like this when we
00:12:51.120 convert it into binary
00:12:53.220 now let's take a closer look at this
00:12:55.260 binary number
00:12:56.639 and let's try to convert this integer
00:12:58.620 back into a floating Point number
00:13:01.800 the sign here is zero meaning that this
00:13:03.720 is a positive number the exponent is 127
00:13:06.959 in decimal
00:13:08.579 which means that the range starts at 2
00:13:10.320 to the power of 0 which is one and ends
00:13:12.779 at 2 to the power of 1 which is 2. and
00:13:15.180 the significant is this 4.19 Million
00:13:18.180 number in decimal
00:13:20.220 we can divide it by 2 to the power of 23
00:13:22.560 which is this 8.38 Million number and we
00:13:25.500 get 0.5 this means that we are halfway
00:13:28.560 in the range between one and two and we
00:13:31.380 can calculate this to be the number 1.5
00:13:34.139 ladies and gentlemen there we have it
00:13:36.240 1.5 is the midpoint between 0 and
00:13:38.820 infinity
00:13:40.980 all right that was a lot to take in
00:13:42.660 let's recap what we did
00:13:45.000 first
00:13:46.079 we took the floating Point endpoints of
00:13:47.940 our range which were the floating Point
00:13:49.380 numbers 0 and infinity and we read these
00:13:52.740 floating Point numbers back as integers
00:13:54.779 which were 0 and 2.1 billion as integers
00:13:58.500 remember that this process is not the
00:14:00.779 same as casting to an integer since what
00:14:03.180 we did here is we directly read the bits
00:14:05.220 of the floating Point number as if it
00:14:06.959 was an integer foreign
00:14:09.060 we then found the midpoint by adding
00:14:11.160 these two numbers and then dividing by
00:14:13.019 two it's this 1.06 billion number in as
00:14:17.760 an integer
00:14:18.959 we then read this integer midpoint back
00:14:21.660 as a floating Point number which is the
00:14:24.180 exact opposite of what we did in step
00:14:26.519 one and we were we got this floating
00:14:28.980 Point number 1.5
00:14:31.980 using this process you can show why the
00:14:34.620 very next number expected which is the
00:14:36.480 midpoint between 1.5 and infinity is
00:14:39.360 this 1.67 times 10 to the power of 154
00:14:42.839 which is a very large number
00:14:44.820 however note that Ruby uses 64-bit
00:14:47.579 floating Point numbers internally
00:14:49.800 which means that in order to calculate
00:14:51.600 this by hand you'll also have to use
00:14:53.459 64-bit floating Point numbers
00:14:55.740 instead of 32-bit floating Point numbers
00:14:57.600 like the examples shown so far
00:15:00.180 but this is left as an exercise for you
00:15:02.279 to do at home
00:15:04.260 all right now you might be asking why
00:15:06.300 does this even work why can we read
00:15:08.579 floating Point numbers as integers and
00:15:11.160 then perform binary search over it
00:15:14.160 recall that the requirements to use
00:15:16.260 binary search is that the list that is
00:15:18.300 being searched on must be either must be
00:15:20.220 sorted in either ascending or descending
00:15:22.440 order
00:15:23.699 while this reading floating Point number
00:15:25.620 is as integers guarantee that it's
00:15:27.660 sorted ascendingly in other words
00:15:30.420 does a larger floating Point number
00:15:32.160 always result in a larger integer when
00:15:35.279 that floating Point number is read as an
00:15:37.380 integer
00:15:39.360 well let's look at the parts of a
00:15:40.800 floating Point number again
00:15:42.600 the significant has 23 bits of the least
00:15:45.120 significant digits
00:15:46.920 we know that the floating Point number
00:15:48.480 with the same sign and exponent but a
00:15:50.880 larger significant will always mean a
00:15:53.160 larger floating Point number in
00:15:54.540 magnitude
00:15:55.800 and a larger significance will result in
00:15:58.320 a larger integer when we read it back as
00:16:00.480 an integer
00:16:01.740 okay so what happens when we increase
00:16:03.839 the exponent
00:16:05.639 well since the exponent determines the
00:16:07.860 range that the floating Point number is
00:16:09.660 in and since the ranges have no overlap
00:16:12.180 a larger exponent will always result in
00:16:15.360 a larger floating Point number in
00:16:17.040 magnitude
00:16:18.180 and of course this results in a larger
00:16:20.279 integer when we read it back as an
00:16:22.079 integer
00:16:23.040 therefore this satisfies the
00:16:24.839 requirements of binary search and this
00:16:26.940 is why we can use this technique to
00:16:28.620 binary search over floating Point
00:16:30.480 numbers
00:16:32.040 foreign
00:16:32.940 let's take a look at the code in Ruby
00:16:35.279 that implements the range B search
00:16:37.079 method
00:16:38.100 the code is inside the Ruby source code
00:16:40.019 which is written in C so it's
00:16:42.000 unfortunately not as easy to read or
00:16:43.980 understand as Ruby code
00:16:46.259 so I've simplified the code and parts
00:16:48.120 have been cut out for Simplicity
00:16:51.180 let's look at the function range B
00:16:53.040 search that implements the Ruby method
00:16:55.800 it accepts one argument which is the
00:16:58.680 range where binary searching over
00:17:01.019 we then get the two endpoints of the
00:17:02.940 range the beginning and the end
00:17:05.579 let's look at the case when at least one
00:17:07.860 of the two endpoints are floating Point
00:17:09.600 numbers since this is the case that we
00:17:11.579 care about
00:17:13.140 using our float value we get the C
00:17:15.660 floating point value from the Ruby float
00:17:18.000 object
00:17:19.319 the more interesting part to this line
00:17:21.059 is the call to the double as int 64
00:17:23.699 function let's take a look at the
00:17:25.740 implementation of that
00:17:27.839 we can see the implementation of it up
00:17:29.700 here
00:17:30.419 it accepts a double Precision floating
00:17:32.520 Point number and returns a 64-bit
00:17:34.799 integer
00:17:36.299 so what does this function do
00:17:38.580 it first creates this local variable
00:17:40.799 called convert which is of this type
00:17:43.140 called Union in 64 double
00:17:45.960 we can see the definition of this Union
00:17:47.820 at the top here
00:17:49.980 it's a union of either a 64-bit integer
00:17:52.500 or a double Precision floating Point
00:17:54.360 number
00:17:55.380 if you're not familiar with unions in C
00:17:57.900 it essentially lets you store any one of
00:18:00.419 these data types at the same memory
00:18:02.160 location
00:18:03.299 it's important to note that it lets you
00:18:05.160 store any one of these data types and
00:18:07.799 not all of them meaning that the size of
00:18:10.260 the Union is only as large as the size
00:18:12.419 of the largest element
00:18:14.280 in this case since both of these types
00:18:16.380 are 64 bits in size the size of this
00:18:18.720 Union is also 64 bits which is 8 bytes
00:18:22.320 let's go back to the double as in64
00:18:24.419 function
00:18:27.179 we then set the double member of the
00:18:29.100 Union
00:18:30.120 we're sending it to the absolute value
00:18:31.980 of the floating Point number using the
00:18:34.080 Fabs function
00:18:35.640 we're sending it to the absolute value
00:18:37.380 due to differences in how integers and
00:18:39.720 floating Point numbers represent
00:18:41.160 negative numbers
00:18:42.840 integers represent negative numbers
00:18:44.820 using the two's complement
00:18:47.160 whereas floating Point numbers only set
00:18:49.320 the negative bit
00:18:50.940 for example this means that for floating
00:18:53.039 Point numbers 1.5 and negative 1.5 will
00:18:56.460 only differ by a single bit which is the
00:18:58.620 sign bit
00:18:59.640 however for integers 10 and negative 10
00:19:02.580 will differ than much more than a single
00:19:04.679 bit
00:19:06.059 I won't be talking about what the two's
00:19:07.860 complemented and why integers use it
00:19:10.200 today but you can look into it if you're
00:19:12.179 interested
00:19:13.380 so in order to not worry about this
00:19:15.120 difference we use the absolute value
00:19:17.100 here
00:19:18.660 we then read the union back as an
00:19:21.120 integer and setting it to negative as
00:19:23.280 required
00:19:24.960 in the previous line we set the union
00:19:26.700 from the floating Point number and here
00:19:29.100 we read it back as an integer
00:19:31.559 note that we're doing this in order to
00:19:33.419 not perform any casting since casting
00:19:36.000 will attempt to convert the floating
00:19:38.039 Point number into an integer rather than
00:19:40.200 directly reading it as an integer
00:19:43.980 now let's go back to the range B search
00:19:46.500 function we then perform binary search
00:19:49.320 over the 64-bit integers which were read
00:19:51.600 from the floating Point endpoints
00:19:53.580 all right hopefully you followed along
00:19:55.380 and have a high level understanding of
00:19:57.360 how this code works
00:19:59.640 if you're interested in playing around
00:20:01.260 with floating Point numbers I highly
00:20:03.299 recommend this site called float.exposed
00:20:05.940 it gives you lots of useful information
00:20:07.559 about floating Point numbers
00:20:09.840 on this site you can see information
00:20:11.580 such as the floating point value
00:20:15.179 the bits that represent this starting
00:20:16.799 point number
00:20:18.179 the values of the scientists the
00:20:19.980 exponent and the significant as decimal
00:20:22.440 integers
00:20:23.940 and the value of this floating Point
00:20:25.799 number when read as a hexadecimal or
00:20:28.080 decimal integer
00:20:30.480 this talk was adapted from my blog post
00:20:32.400 at this link which you can also access
00:20:34.200 through this QR code
00:20:35.940 feel free to reach out to me through
00:20:37.320 Twitter my handle is peterz118 if you're
00:20:40.200 in the future and watching a recording
00:20:41.760 of this talk hopefully Twitter is still
00:20:43.380 alive by then
00:20:45.059 if not you can always reach out to me
00:20:47.100 through email my email is Peter pjz.ca
00:20:49.860 thank you for listening to my talk
00:21:01.919 we still have a couple of minutes if
00:21:03.600 anybody has any questions I'm uh you can
00:21:05.940 you can ask them now or you can always
00:21:07.860 talk to me after this talk I'll be
00:21:10.500 around in in the conference for the next
00:21:12.299 three days
00:21:13.679 yeah so actually
00:21:16.500 um right before this talk Benoit and I
00:21:19.740 actually looked at the implementation of
00:21:21.240 this function sir oh I need to repeat
00:21:23.880 the question
00:21:25.200 um so the question was why does
00:21:28.380 um Ruby see Ruby specifically do this uh
00:21:33.059 floating point to integer conversion
00:21:35.120 instead of
00:21:36.900 instead of just directly searching over
00:21:38.700 floating Point numbers
00:21:41.100 um and the answer is
00:21:44.159 um Benoit and I actually looked at the
00:21:46.799 implementation of this function in
00:21:48.539 truffle Ruby before this talk
00:21:50.760 and um
00:21:52.740 interestingly enough they because it's
00:21:56.340 written in Ruby it's so much more easy
00:21:59.340 to to understand in Ruby and it doesn't
00:22:02.100 do this kind of
00:22:03.900 you might call it a dirty trick
00:22:06.120 um what what they do instead is they
00:22:09.000 first check if uh if the number is
00:22:12.539 infinity and so so then so then they
00:22:15.840 know if it's infinity or not and if it's
00:22:17.700 not Infinity then they binary search
00:22:19.559 between zero and the largest floating
00:22:22.919 Point number possible and then you can
00:22:25.260 perform binary search because you can
00:22:26.760 divide the largest floating Point number
00:22:28.559 by two whereas you cannot divide
00:22:30.480 Infinity by two and so Ruby could have
00:22:33.960 done something like that but because
00:22:36.780 it's C so they wanted to do this trick
00:22:39.659 that that is very clever
00:22:42.419 um
00:22:43.080 but instead is is rather difficult to
00:22:46.020 read and um and has seemingly arbitrary
00:22:49.559 output
00:22:51.179 so so here we have Infinity right
00:22:53.100 Infinity is zero uh from the sign the
00:22:57.299 the exponent is all ones and all zero is
00:22:59.700 significant
00:23:01.080 for not a number it is
00:23:04.020 um it is also a so I have a pointer here
00:23:07.500 if you can see it um the exponents is
00:23:10.020 all ones but we have a non-zero
00:23:11.400 significant so there's actually more
00:23:13.260 than one way to denote not a number
00:23:15.960 uh in fact here there would be 2 to the
00:23:18.900 power of 23 minus 1 ways to denote not a
00:23:23.039 number
00:23:24.240 yeah so the question was why is there
00:23:26.159 eight million ways to to denote not a
00:23:28.679 number
00:23:29.400 and and the
00:23:31.200 answer to that is
00:23:33.600 um there's no
00:23:36.480 there's there's so
00:23:40.320 you you'll have to ask IEEE for that
00:23:43.500 hahaha
00:23:48.840 okay the question was do I think not a
00:23:51.419 number should be a falsy value and I'm
00:23:53.520 assuming you mean that in Ruby
00:23:56.100 uh
00:23:57.419 uh no because
00:24:00.200 falsely because this isn't JavaScript so
00:24:02.940 the only falsy value should be false in
00:24:04.740 a nil and we don't need more falsy
00:24:07.200 values
00:24:13.140 all right thank you very much