Expanding on what
@omar.fiscal wrote, for those that want to go deeper ...
When I saw this thread, the first thing I noticed was how close the "cap" was to 64 million, which meant it was around 4 (=2
2) time 16,777,216 (=2
24 = 0x1000000).
So the first thing I tried was putting that cap value into a calculator in Programming Mode, which confirmed that it was a power of 2:
The significance of 16,777216 (2
24[) is that is the largest whole number, represented in 32-bit REAL (IEEE-754 Single-Precision Floating-Point) that can be the result of adding 1 to another whole number, because the mantissa of 32-bit reals has 24 bits available (with another 8 for the exponent and 1 for the sign).
Anyway, a slightly more interesting value is 67,108,860, four less than the ...,864 cap that spawned this thread all those years ago.
Note especially
- that the last two binary digits (bits), highlighted in green, are 0, and
- that the next 24 bits, highlighted in red, are all 1s.
The primary issue of this thread is analogous to a decimal display using scientific notation with two digit places (a ones digit and a tenths digit) and a power-of-10 place e.g. "9.8 x 10
3"). When the display is "9.8 x 10
3" the value is 9800; the next two greater and closest values that can be displayed are "9.9x 10
3" (9900) and "1.0 x 10
4" (10000), i.e. the displayable values increment in steps of 100 (= 0.1x 10
3). After 1.0 x 10
4 however, the next greater and closest value is 1.1x 10
4 i.e. a step of 1000 (= 0.1x 10
4). The "00" and "000" suffixes are the ones, tens, and hundred digits, assume to be 0 because they are after the last displayed (tenths) digit of the scientific notation.
In 32-bit REAL, the corresponding values are
Decimal___base-2 scientific notation
67,108,856 1.11111111111111111111110 x 225
67,108,860 1.11111111111111111111111 x 225
67,108,864 1.00000000000000000000000 x 226
67,108,872 1.00000000000000000000001 x 226
As a final, if pedantic, example of what is happening, consider the following Python code and output:
>>> import numpy
>>> f=numpy.float32
>>> for i in range(67108856,67108881): print((i,int(f(i)),i==int(f(i)) and '==' or ' ',bin(i),bin(int(f(i))),))
...
(67108856, 67108856, '==', '0b11111111111111111111111000', '0b11111111111111111111111000')
(67108857, 67108856, ' ', '0b11111111111111111111111001', '0b11111111111111111111111000')
(67108858, 67108856, ' ', '0b11111111111111111111111010', '0b11111111111111111111111000')
(67108859, 67108860, ' ', '0b11111111111111111111111011', '0b11111111111111111111111100')
(67108860, 67108860, '==', '0b11111111111111111111111100', '0b11111111111111111111111100')
(67108861, 67108860, ' ', '0b11111111111111111111111101', '0b11111111111111111111111100')
(67108862, 67108864, ' ', '0b11111111111111111111111110', '0b100000000000000000000000000')
(67108863, 67108864, ' ', '0b11111111111111111111111111', '0b100000000000000000000000000')
[B][COLOR=rgb(44, 130, 201)](67108864, 67108864, '==', '0b100000000000000000000000000', '0b100000000000000000000000000')[/COLOR][/B]
(67108865, 67108864, ' ', '0b100000000000000000000000001', '0b100000000000000000000000000')
(67108866, 67108864, ' ', '0b100000000000000000000000010', '0b100000000000000000000000000')
(67108867, 67108864, ' ', '0b100000000000000000000000011', '0b100000000000000000000000000')
(67108868, 67108864, ' ', '0b100000000000000000000000100', '0b100000000000000000000000000')
(67108869, 67108872, ' ', '0b100000000000000000000000101', '0b100000000000000000000001000')
(67108870, 67108872, ' ', '0b100000000000000000000000110', '0b100000000000000000000001000')
(67108871, 67108872, ' ', '0b100000000000000000000000111', '0b100000000000000000000001000')
(67108872, 67108872, '==', '0b100000000000000000000001000', '0b100000000000000000000001000')
(67108873, 67108872, ' ', '0b100000000000000000000001001', '0b100000000000000000000001000')
(67108874, 67108872, ' ', '0b100000000000000000000001010', '0b100000000000000000000001000')
(67108875, 67108872, ' ', '0b100000000000000000000001011', '0b100000000000000000000001000')
(67108876, 67108880, ' ', '0b100000000000000000000001100', '0b100000000000000000000010000')
(67108877, 67108880, ' ', '0b100000000000000000000001101', '0b100000000000000000000010000')
(67108878, 67108880, ' ', '0b100000000000000000000001110', '0b100000000000000000000010000')
(67108879, 67108880, ' ', '0b100000000000000000000001111', '0b100000000000000000000010000')
(67108880, 67108880, '==', '0b100000000000000000000010000', '0b100000000000000000000010000')
The first column is a source integer incrementing by one from 67,108,856 to 67,108,880, i.e. from eight less than, to 16 greater than, the 67,108,864 cap. The second column is the result of that integer converted to 32-bit float (REAL) and then back to integer i.e. analogous to values written in the x.y x 10
z display format described above and then back to an integer (e.g. 10,501 => 1.1 x 10
4 => 11,000). The third value is '==' or ' ' if the original value is equal or not equal, respectively, to the converted value. The fourth and fifth columns are the original and converted integer values expressed in binary.
Since only the 24 most-significant digits of the original integer value can be stored and preserved in the 32-bit REAL, some number of least-significant bits are truncated (lost) in the conversion, as can be seen by comparing the fourth and fifth columns:
- two bits are truncated above the 67,108,864 cap
- the last two bits are always 00 in the fifth column;
- three bits are truncated below the cap
- the last three bits are always '000' in the fifth column