Eu esperaria que isso só acontecesse quando o último bit da mantissa fosse 0
. Caso contrário, para subtraí-los (já que seus expoentes diferem em 1), x
perderia um pouco de precisão primeiro e o resultado acabaria sendo arredondado para cima ou para baixo.
Mas um experimento rápido mostra que isso parece sempre valer (assumindo que x
e 2x
são finitos) para qualquer número aleatório (incluindo aqueles com um 1
bit à direita).
import random
import struct
from collections import Counter
def float_to_bits(f: float) -> int:
"""
Convert a double-precision floating-point number to a 64-bit integer.
"""
# Pack the float into 8 bytes, then unpack as an unsigned 64-bit integer
return struct.unpack(">Q", struct.pack(">d", f))[0]
def check_floating_point_precision(num_trials: int) -> float:
true_count = 0
false_count = 0
bit_counts = Counter()
for _ in range(num_trials):
x = random.uniform(0, 1)
if 2 * x - x == x:
true_count += 1
else:
false_count += 1
bits = float_to_bits(x)
# Extract the last three bits of the mantissa
last_three_bits = bits & 0b111
bit_counts[last_three_bits] += 1
return (bit_counts, true_count / num_trials)
num_trials = 1_000_000
(bit_counts, proportion_true) = check_floating_point_precision(num_trials)
print(f"The proportion of times 2x - x == x holds true: {proportion_true:.6f}")
print("Distribution of last three bits (mod 8):")
for bits_value in range(8):
print(f"{bits_value:03b}: {bit_counts[bits_value]} occurrences")
The proportion of times 2x - x == x holds true: 1.000000
Distribution of last three bits (mod 8):
000: 312738 occurrences
001: 62542 occurrences
010: 125035 occurrences
011: 62219 occurrences
100: 187848 occurrences
101: 62054 occurrences
110: 125129 occurrences
111: 62435 occurrences