Floating-Point
FleetingThe book https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html is a must read about the subject.
Also, the site https://0.30000000000000004.com/ provides examples in several programming languages that help grasp the issue.
issues
floating-point issues
2**m * 2^(e)
m < 2**53 e < 2**1023
big numbers EAT small numbers
the precision of a double is a most 53 bits, meaning
return 1. + 2 ** 53 - 2**53
0.0
while
return 1. + 2 ** 52 - 2**52
1.0
import math
return math.log10(2**53)
15.954589770191003
Only 15 decimal digits of precision, so
return 1. + 10**16 - 10**16
0.0
while
return 1. + 10**15 - 10**15
1.0
error can accumulate
return 0.1 + 0.1 + 0.1, 0.3,
(0.30000000000000004, 0.3)
import math
def ratio(f):
r = float.as_integer_ratio(f)
return r[0], math.log2(r[1])
return ratio(0.1), ratio(0.2), ratio(0.3), ratio(0.1 + 0.1 + 0.1)
((3602879701896397, 55.0), (3602879701896397, 54.0), (5404319552844595, 54.0), (1351079888211149, 52.0))
Of course, 0.2 = 2 * 0.1, so the mantissa remains the same and the power of two that multiply it is simply decreased.
To do floating-point addition, one reduce the two numbers to the same exponent and then perform the integer addition on the mantissa, then normalize the power of two by dividing as much as possible by two.
So 0.1 + 0.2 means doing this
return 3602879701896397 * 2 + 3602879701896397, 55
10808639105689191 | 55 |
And then dividing by twos
return result[0] / 8, result[1] - 3
(1351079888211149.0, 52)
Which is what we found earlier, and is not the closest approximation of 0.3.
how far can we make the error big
People warn about how the error is an issue, but as far as I can tell, I could not find an error that lost more than 10 digits of precision. Can we actually loose moreĀ ?
value = 0.
number = 10**8
for i in range(number):
value += 0.1
return value, number / 10, abs((value - number / 10) / value)
(9999999.98112945, 10000000.0, 1.88705493120809e-09)
maximal mantissa per precision
floating-point maximal mantissa per precision
error simply put
floating-points number are not able to encode 0.1.
import decimal
decimal.Decimal(0.1)
0.1000000000000000055511151231257827021181583404541015625
Therefore, 0.1 added 10 times does not equal to 1.0
0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 == 1.0
False
But, due to precision approximation, 0.9 and 0.1 equal to 1.0.
0.9 + 0.1 == 1.0
True
Notes linking here
- don’t store ethers or wei as simple integer or floats
- javascript integers and precision
- number in javascript