부동소수점 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
"""부동소수점 문서의 값 확인.
문서: 03.부동소수점
주장:
1. 고정소수점 0.1011(2) = 0.6875. 4.50 + 2.75 = 7.25 = 111.01(2), 4.50 x 2.75 = 12.375 = 1100.011(2).
2. 단정도 비트 0 01111100 0100...0 = 0.15625 (부호 0, 지수 124 - 127 = -3, 가수 1.25).
3. 단정도의 끝값: 1 + 2^-23, 2 - 2^-23, 2^-126 = 1.17549435e-38, 2^127 = 1.70141183e38, 최댓값 3.40e38.
4. 배정도 3FD5 5555 5555 5555 = 2^-2 x 1.333... = 0.333333333333333314829616256247...
5. 배정도는 -2^53 ~ 2^53의 정수를 정확히 적는다. 2^53 + 1은 적지 못한다. 2^53 ~ 2^54는 짝수만.
6. 이웃한 두 수의 간격은 값이 클수록 넓다: 1 근처 2^-52, 2^52 근처 1. 단정도는 1 근처 2^-23, 2^24 근처 2.
7. 유효 자릿수: 24 x log10(2) = 7.22, 53 x log10(2) = 15.95.
8. 0.1 + 0.2 != 0.3.
실행: python3 03_floating-point_verify.py
"""
import math
import struct
from fractions import Fraction
def f32_from_bits(bits):
return struct.unpack(">f", int(bits, 2).to_bytes(4, "big"))[0]
def f32_bits(x):
return format(int.from_bytes(struct.pack(">f", x), "big"), "032b")
def f32(x):
return struct.unpack(">f", struct.pack(">f", x))[0]
def decode32(bits):
s, e, f = int(bits[0]), int(bits[1:9], 2), int(bits[9:], 2)
return (-1) ** s * Fraction(2) ** (e - 127) * (1 + Fraction(f, 2 ** 23))
def bin_frac(s):
ip, fp = s.split(".")
return int(ip, 2) + sum(int(b) * Fraction(1, 2 ** (i + 1)) for i, b in enumerate(fp))
if __name__ == "__main__":
assert bin_frac("0.1011") == Fraction(11, 16) == Fraction("0.6875")
assert bin_frac("100.1") == Fraction("4.5") and bin_frac("10.11") == Fraction("2.75")
assert bin_frac("111.01") == Fraction("7.25") and bin_frac("1100.011") == Fraction("12.375")
assert Fraction("4.5") * Fraction("2.75") == Fraction("12.375")
b = "0" + "01111100" + "01" + "0" * 21
assert len(b) == 32 and f32_from_bits(b) == 0.15625 and decode32(b) == Fraction(5, 32)
assert int("01111100", 2) == 124 and 124 - 127 == -3 and 1.25 * 2 ** -3 == 0.15625
assert abs((1 + 2 ** -23) - 1.000000119) < 1e-9
assert abs((2 - 2 ** -23) - 1.999999881) < 1e-9
assert abs(2 ** -126 / 1.17549435e-38 - 1) < 1e-8
assert abs(2 ** 127 / 1.70141183e38 - 1) < 1e-8
mx = (2 - 2 ** -23) * 2.0 ** 127
assert abs(mx / 3.40e38 - 1) < 1e-2 and f32(mx) == mx
x = struct.unpack(">d", bytes.fromhex("3FD5555555555555"))[0]
assert x == 1 / 3
assert format(Fraction(x).numerator * 10 ** 30 // Fraction(x).denominator, "d").startswith("333333333333333314829616256247")
e = (int("3FD", 16)) - 1023
assert e == -2
assert float(2 ** 53) == 2 ** 53 and float(2 ** 53 + 1) == 2 ** 53
assert float(2 ** 53 + 2) == 2 ** 53 + 2
for k in range(-1000, 1001):
assert int(float(2 ** 53 - abs(k))) == 2 ** 53 - abs(k)
assert math.ulp(1.0) == 2 ** -52 and math.ulp(2.0 ** 52) == 1.0
assert f32(1 + 2 ** -23) != 1.0 and f32(1 + 2 ** -25) == 1.0
assert f32(2 ** 24 + 1) == 2 ** 24 and f32(2 ** 24 + 2) == 2 ** 24 + 2
assert f32(16777217.0) == 16777216.0
assert round(24 * math.log10(2), 2) == 7.22 and round(53 * math.log10(2), 2) == 15.95
assert 0.1 + 0.2 != 0.3 and abs(0.1 + 0.2 - 0.3) < 1e-15
# 카드: -6.5 = -110.1(2) = -1.101 x 2^2 -> 부호 1, 지수 129 = 10000001, 가수 101000...
assert f32_bits(-6.5) == "1" + "10000001" + "101" + "0" * 20
# 카드: 0.1은 2진 유한 소수가 아니다
assert Fraction(0.1) != Fraction(1, 10)
# 진법과 자릿수 문서의 카드 C4 (다): 0.10000001(2) = 1/2 + 1/256
assert bin_frac("0.10000001") == Fraction("0.50390625")
print("ALL CHECKS PASSED")