فهرست مقاله
بازبینیِ یک آزمایش عددی، فقط دیدنِ پیامِ موفقیتِ برنامه نیست. فرض کنید در یک قابِ ساختگی، باقیماندهٔ ولتاژِ پیشبینیشده و مقدارِ قراردادی را جمع میکنیم. نتیجه قرار است همراهِ نسخهٔ کد ثبت شود تا تغییرِ بعدی قابل بررسی باشد. پیش از طراحیِ چنین کنترلِ رگرسیونی، باید روشن کنیم «جوابِ درست» به عددهای واقعاً دریافتشده اشاره دارد یا به مقدارهای اعشاریِ مورد نظرِ نویسنده. این دو هدف همیشه یکسان نیستند.
در این مثال، هیچ حسگر، سرویسِ یکپارچهسازی مداوم یا تجهیزی اجرا نشده است. ورودیها تألیفیاند و واحدشان میلیولت است. مقایسهٔ ما فقط دربارهٔ محاسبهٔ یک جمع است؛ از آن، صحتِ مدل، دقتِ فیزیکی، میانگینِ آماری یا سرعتِ اجرای یک خطِ تحویل نتیجه نمیگیریم. تصاویرِ همراه نیز مفهوم را نشان میدهند و نمودارِ نتیجهٔ آزمایش نیستند.
۱. قابِ باقیمانده و واحدِ میلیولت

قاب، یک list یا tuple با حداکثر شانزده عضو است. هر عضو باید دقیقاً float داخلیِ پایتون، متناهی و در محدودهٔ قدرمطلقِ دو به توان پنجاهوچهار باشد. این سقفِ بزرگ، مرزِ آزمونِ تنشِ عددی است و محدودهٔ کارِ ابزارِ ولتاژ نیست. مقدارِ مثبت و منفی مجاز است؛ علامتِ باقیمانده بخشی از داده است و پیش از جمع حذف نمیشود.
رشته، عددِ صحیح، بولی، کسر و مولد بهطور ضمنی تبدیل نمیشوند. حتی عددِ صحیحِ ظاهراً ساده ممکن است هنگامِ تبدیل به float اطلاعاتی از دست بدهد؛ رابطِ محدودِ ما میخواهد همان نوعِ ورودیِ موردِ بازبینی را آشکار نگه دارد. قابِ تهی نیز مجاز است و جمعِ صفر دارد. صفرِ این قرارداد به معنیِ انجامِ یک اندازهگیریِ واقعی یا مشاهدهٔ نبودِ خطا نیست.
جمع، همچنان واحدِ میلیولت دارد. برای میانگین، تقسیم بر شمار لازم است؛ برای انتگرال، زمان و قاعدهٔ نمونهبرداری باید تعریف شود. هیچکدام در این تابع وجود ندارد. همچنین ممکن است باقیماندههای بزرگِ مثبت و منفی، جمعِ کوچکی داشته باشند. کوچک بودنِ این یک عدد، کوچک بودنِ همهٔ مؤلفهها یا مناسب بودنِ مدل را ثابت نمیکند.
۲. حلقهٔ ساده و جمعِ دقیقتر

مسیرِ مقایسه را عمداً با متغیرِ صفر و تکرارِ total += value مینویسیم. هر گام یک جمعِ ممیزشناورِ تازه است. با سه ورودیِ دو به توان پنجاهوسه، یک و منفیِ دو به توان پنجاهوسه، جمعِ دقیق یک است؛ ولی گامِ اولِ این ترتیب میتواند سهمِ یک را از مقدارِ میانی حذف کند. تغییرِ ترتیب، مسیرِ گردشدن را عوض میکند.
طبقِ قراردادِ کاملِ math.fsum، این تابع چند جمعِ جزئی را نگه میدارد تا اتلافِ دقت کاهش یابد. همان مدخل، وابستگی به حسابِ شناور و احتمالِ گردشدنِ دوباره در بعضی ساختها را هم بیان میکند. بنابراین نتیجهٔ خانوادهٔ محدودِ این برنامه را تضمینِ گردکردنِ درست برای تمامِ ورودیها و تمامِ سکوها معرفی نمیکنیم.
مسیرِ سادهٔ بالا، تابعِ داخلیِ sum نیست. فصلِ ممیزشناورِ پایتون دربارهٔ بهبودِ جمعِ داخلی و تفاوتِ آن با جمعِ پیاپی توضیح میدهد. نسبت دادنِ خروجیِ حلقهٔ ما به sum نسخهٔ فعلی، آزمایشِ دیگری را به جای این آزمایش مینشاند. اینجا نیز زمانسنجی یا مقایسهٔ کارایی انجام نمیشود.
۳. کدِ کامل و مرجعِ کسریِ مستقل

فایل را با نامِ fsum_frame_regression.py ذخیره کنید. وابستگی فقط کتابخانهٔ استاندارد است و نسخهٔ واقعیِ اجرا در خروجی میآید. برنامه محیطِ دودویی با دقتِ پنجاهوسه بیت را کنترل میکند. مرجع، هر float را جداگانه با Fraction.from_float به همان مقدارِ گویای واقعی تبدیل و سپس با حسابِ کسری جمع میکند؛ نه fsum را فراخوانی میکند و نه حلقهٔ جمعِ شناورِ اصلی را تکرار میکند.
مستنداتِ Fraction و مدخلِ from_float فرقِ این تبدیل با ساختنِ کسر از متنِ اعشاری را روشن میکند. برای قابِ کوچکِ حاضر، هزینهٔ مرجعِ دقیق قابل کنترل است؛ این انتخاب، توصیهٔ ذخیره و محاسبهٔ کسری برای هر جریانِ نامحدود نیست. خطای مطلق نیز با کسر محاسبه میشود تا تفریقِ دو تقریبِ شناور، خودِ گزارشِ خطا را پنهان نکند.
"""Finite synthetic millivolt residual sums; no measured device or CI service."""
from fractions import Fraction
from itertools import permutations, product
import json
import math
import platform
import sys
MAX_COUNT = 16
MAX_ABS_MV = float(2**54) # Numerical stress cap, not a physical voltage range.
def checked_frame(readings_mv):
if type(readings_mv) not in (list, tuple):
raise TypeError("An exact list or tuple is required")
if len(readings_mv) > MAX_COUNT:
raise ValueError("At most 16 supplied residuals are accepted")
values = tuple(readings_mv)
for value in values:
if type(value) is not float:
raise TypeError("Each supplied value must be an exact builtin float")
if not math.isfinite(value) or abs(value) > MAX_ABS_MV:
raise ValueError("Finite values within the declared stress cap required")
return values
def sum_frame_mv(readings_mv):
"""Use fsum on already supplied floats; do not coerce or infer sensor accuracy."""
values = checked_frame(readings_mv)
return {"count": len(values), "sum_mv": math.fsum(values)}
def exact_input_reference(values):
# Independent rational arithmetic: no fsum, float accumulation or main call.
total = Fraction(0)
for value in values:
total += Fraction.from_float(value)
return total
def sequential_loop(values):
# Explicit repeated binary additions. This is NOT Python's builtin sum().
total = 0.0
for value in values:
total += value
return total
def frame_report(readings_mv):
values = checked_frame(readings_mv)
result = sum_frame_mv(values)
exact = exact_input_reference(values)
loop = sequential_loop(values)
fsum_error = abs(Fraction.from_float(result["sum_mv"]) - exact)
loop_error = abs(Fraction.from_float(loop) - exact)
return {
"readings_mv": list(values), "count": result["count"],
"sequential_loop_sum_mv": loop, "fsum_mv": result["sum_mv"],
"exact_supplied_binary_sum_mv": str(exact),
"rounded_rational_reference_mv": float(exact),
"fsum_matches_rounded_reference_here": result["sum_mv"] == float(exact),
"absolute_fsum_error_exact_mv": str(fsum_error),
"absolute_loop_error_exact_mv": str(loop_error),
"relative_fsum_error_exact": str(fsum_error / abs(exact)) if exact else None,
"relative_loop_error_exact": str(loop_error / abs(exact)) if exact else None,
}
def rejects(call, exception):
try:
call()
except exception:
return True
return False
def finite_family():
alphabet = (-float(2**53), -1.0, -0.5, 0.0, 0.5, 1.0, float(2**53))
cases = 0
mismatches = 0
rounded_nonexact = 0
largest_exact_error = Fraction(0)
for length in range(5):
for values in product(alphabet, repeat=length):
actual = sum_frame_mv(values)["sum_mv"]
reference = exact_input_reference(values)
error = abs(Fraction.from_float(actual) - reference)
cases += 1
mismatches += actual != float(reference)
rounded_nonexact += error != 0
largest_exact_error = max(largest_exact_error, error)
return {
"alphabet_mv": list(alphabet), "lengths": [0, 1, 2, 3, 4],
"cases": cases, "check_groups_counted": 1,
"rounded_reference_mismatches": mismatches,
"nonzero_exact_output_rounding_errors": rounded_nonexact,
"largest_absolute_fsum_error_exact_mv": str(largest_exact_error),
}
def run_example():
assert sys.float_info.radix == 2 and sys.float_info.mant_dig == 53
a = float(2**53)
cancellation = frame_report([a, 1.0, -a])
orders = list(permutations((a, 1.0, -a)))
loops = sorted({sequential_loop(x) for x in orders})
fsums = sorted({sum_frame_mv(x)["sum_mv"] for x in orders})
empty = frame_report([])
zero = frame_report([0.1, -0.1, -0.0])
dyadic = (0.5, 0.25, -0.125)
unit_microvolts = tuple(x * 1000.0 for x in dyadic)
decimal_values = (0.1, 0.1, 0.1)
decimal_case = frame_report(decimal_values)
binary_tenth = Fraction.from_float(0.1)
intended_decimal = Fraction(3, 10)
lost_integer = 2**54
lost_values = (float(lost_integer + 1), -float(lost_integer))
signed = (a, 0.5, -a, 0.125)
family = finite_family()
before = [0.5, 0.25, -0.125]
first = frame_report(before)
first["readings_mv"][0] = 999.0
second = frame_report(before)
bad_types = (True, 1, "0.5", 0.5+0j, Fraction(1, 2), None)
checks = {
"manual_cancellation_retains_one_millivolt":
cancellation["sequential_loop_sum_mv"] == 0.0
and cancellation["fsum_mv"] == 1.0
and cancellation["exact_supplied_binary_sum_mv"] == "1",
"six_selected_orders_expose_explicit_loop_sensitivity":
len(orders) == 6 and loops == [0.0, 1.0] and fsums == [1.0],
"empty_frame_has_zero_without_relative_division":
empty["count"] == 0 and empty["fsum_mv"] == 0.0
and empty["relative_fsum_error_exact"] is None,
"singleton_preserves_the_supplied_binary_value":
sum_frame_mv([0.1])["sum_mv"] == 0.1
and exact_input_reference((0.1,)) == binary_tenth
and frame_report([0.1])["absolute_fsum_error_exact_mv"] == "0",
"dyadic_hand_total_and_this_unit_conversion_are_exact":
exact_input_reference(dyadic) == Fraction(5, 8)
and sum_frame_mv(dyadic)["sum_mv"] == 0.625
and exact_input_reference(unit_microvolts) == 625
and math.fsum(unit_microvolts) == 625.0,
"nonempty_exact_zero_reports_absolute_error_only":
zero["count"] == 3 and zero["exact_supplied_binary_sum_mv"] == "0"
and zero["absolute_fsum_error_exact_mv"] == "0"
and zero["relative_fsum_error_exact"] is zero["relative_loop_error_exact"] is None,
"lost_input_distinction_is_not_recovered_by_summation":
float(lost_integer + 1) == float(lost_integer)
and exact_input_reference(lost_values) == 0
and sum_frame_mv(lost_values)["sum_mv"] == 0.0
and (lost_integer + 1) - lost_integer == 1,
"decimal_intent_and_binary_reference_are_different_targets":
binary_tenth == Fraction(3602879701896397, 2**55)
and binary_tenth != Fraction("0.1")
and exact_input_reference(decimal_values) != intended_decimal
and decimal_case["fsum_matches_rounded_reference_here"]
and decimal_case["absolute_fsum_error_exact_mv"] != "0",
"sign_reflection_preserves_this_selected_reference_and_sum":
exact_input_reference(tuple(-x for x in signed)) == -Fraction(5, 8)
and sum_frame_mv(signed)["sum_mv"] == 0.625
and sum_frame_mv(tuple(-x for x in signed))["sum_mv"] == -0.625,
"balanced_large_pair_keeps_the_selected_small_total":
exact_input_reference((a,) + dyadic + (-a,)) == Fraction(5, 8)
and sum_frame_mv((a,) + dyadic + (-a,))["sum_mv"] == 0.625,
"declared_container_and_count_boundary_are_enforced":
sum_frame_mv(tuple([0.0] * 16))["count"] == 16
and rejects(lambda: sum_frame_mv([0.0] * 17), ValueError)
and rejects(lambda: sum_frame_mv(iter([0.0])), TypeError),
"strict_builtin_float_policy_rejects_silent_coercions":
all(rejects(lambda x=x: sum_frame_mv([x]), TypeError) for x in bad_types),
"finite_magnitude_cap_includes_endpoints_and_small_values":
sum_frame_mv([MAX_ABS_MV, -MAX_ABS_MV])["sum_mv"] == 0.0
and sum_frame_mv([2.0**-1074])["sum_mv"] == 2.0**-1074
and all(rejects(lambda x=x: sum_frame_mv([x]), ValueError)
for x in (math.nan, math.inf, -math.inf, MAX_ABS_MV * 2.0)),
"all_2801_small_frames_match_independent_rounding_and_fresh_state":
family["cases"] == 2801 and family["rounded_reference_mismatches"] == 0
and before == [0.5, 0.25, -0.125]
and second["readings_mv"] == before and second["fsum_mv"] == 0.625,
}
assert len(checks) == 14 and all(type(x) is bool for x in checks.values())
assert all(checks.values()), checks
return {
"python": platform.python_version(), "implementation": platform.python_implementation(),
"dependencies": "standard library only", "units": "synthetic residual millivolts",
"float_runtime": {"radix": sys.float_info.radix, "mant_dig": sys.float_info.mant_dig},
"own_bounds": {"container": "exact list or tuple", "item_type": "exact builtin float",
"count_inclusive": [0, MAX_COUNT], "absolute_input_cap_mv": MAX_ABS_MV,
"stress_cap_is_not_a_physical_device_range": True},
"cancellation": cancellation,
"six_orders": {"cases": len(orders), "explicit_loop_outputs_mv": loops, "fsum_outputs_mv": fsums},
"dyadic_units": {"sum_mv": 0.625, "same_fixture_sum_uv": 625.0},
"decimal_intent_counterexample": {
"actual_binary_tenth_mv": str(binary_tenth),
"intended_decimal_sum_mv": str(intended_decimal), "actual_input_report": decimal_case},
"representation_loss_before_algorithm": {
"intended_integer_difference_mv": 1, "supplied_float_values_mv": list(lost_values),
"exact_supplied_difference_mv": str(exact_input_reference(lost_values))},
"empty": empty, "nonempty_zero": zero,
"independent_finite_family_ONE_group": family,
"unique_check_groups": len(checks), "checks": checks,
"limits": ["Observed finite fixtures, not a universal correct-rounding proof or cross-version output guarantee.",
"The baseline is explicit +=, not Python's current compensated builtin sum.",
"Exact reference targets already supplied binary floats, not intended decimals or sensor truth.",
"No physical measurement, device range, CI service, benchmark or timing was tested."],
}
if __name__ == "__main__":
sys.stdout.buffer.write((json.dumps(run_example(), ensure_ascii=True, indent=2) + "\n").encode("utf-8"))
چهارده گروهِ نامدار، شاهدهای دستی، ترتیبِ جمع، واحد، ورودیِ نامعتبر و مرزهای رابط را پوشش میدهند. خانوادهٔ مستقل، تمامِ قابهای طولِ صفر تا چهار را روی هفت مقدارِ اعلامشده میسازد؛ در مجموع ۲۸۰۱ قاب و فقط یک گروهِ آزمون. مقایسه با float کردنِ جمعِ گویای دقیق انجام میشود. این هدف، با برابر بودنِ خودِ عددِ شناور و کسرِ دقیق فرق دارد.
گزارشِ کامل، خروجیِ واقعیِ همان اجرا را نگه میدارد. تعدادِ جمعهای کسریِ غیرقابل نمایشِ دقیق، خطای مطلقِ کسری و نتیجهٔ مقایسه با مرجعِ گردشده جدا ثبت میشوند. شمارِ قابها، تعدادِ گروههای مستقل یا تعدادِ اجرای نویسنده را افزایش نمیدهد. ورودیِ فراخواننده نیز عوض نمیشود و فهرستِ خروجی، یک کپیِ تازه است.
{
"python": "3.14.5",
"implementation": "CPython",
"dependencies": "standard library only",
"units": "synthetic residual millivolts",
"float_runtime": {
"radix": 2,
"mant_dig": 53
},
"own_bounds": {
"container": "exact list or tuple",
"item_type": "exact builtin float",
"count_inclusive": [
0,
16
],
"absolute_input_cap_mv": 1.8014398509481984e+16,
"stress_cap_is_not_a_physical_device_range": true
},
"cancellation": {
"readings_mv": [
9007199254740992.0,
1.0,
-9007199254740992.0
],
"count": 3,
"sequential_loop_sum_mv": 0.0,
"fsum_mv": 1.0,
"exact_supplied_binary_sum_mv": "1",
"rounded_rational_reference_mv": 1.0,
"fsum_matches_rounded_reference_here": true,
"absolute_fsum_error_exact_mv": "0",
"absolute_loop_error_exact_mv": "1",
"relative_fsum_error_exact": "0",
"relative_loop_error_exact": "1"
},
"six_orders": {
"cases": 6,
"explicit_loop_outputs_mv": [
0.0,
1.0
],
"fsum_outputs_mv": [
1.0
]
},
"dyadic_units": {
"sum_mv": 0.625,
"same_fixture_sum_uv": 625.0
},
"decimal_intent_counterexample": {
"actual_binary_tenth_mv": "3602879701896397/36028797018963968",
"intended_decimal_sum_mv": "3/10",
"actual_input_report": {
"readings_mv": [
0.1,
0.1,
0.1
],
"count": 3,
"sequential_loop_sum_mv": 0.30000000000000004,
"fsum_mv": 0.30000000000000004,
"exact_supplied_binary_sum_mv": "10808639105689191/36028797018963968",
"rounded_rational_reference_mv": 0.30000000000000004,
"fsum_matches_rounded_reference_here": true,
"absolute_fsum_error_exact_mv": "1/36028797018963968",
"absolute_loop_error_exact_mv": "1/36028797018963968",
"relative_fsum_error_exact": "1/10808639105689191",
"relative_loop_error_exact": "1/10808639105689191"
}
},
"representation_loss_before_algorithm": {
"intended_integer_difference_mv": 1,
"supplied_float_values_mv": [
1.8014398509481984e+16,
-1.8014398509481984e+16
],
"exact_supplied_difference_mv": "0"
},
"empty": {
"readings_mv": [],
"count": 0,
"sequential_loop_sum_mv": 0.0,
"fsum_mv": 0.0,
"exact_supplied_binary_sum_mv": "0",
"rounded_rational_reference_mv": 0.0,
"fsum_matches_rounded_reference_here": true,
"absolute_fsum_error_exact_mv": "0",
"absolute_loop_error_exact_mv": "0",
"relative_fsum_error_exact": null,
"relative_loop_error_exact": null
},
"nonempty_zero": {
"readings_mv": [
0.1,
-0.1,
-0.0
],
"count": 3,
"sequential_loop_sum_mv": 0.0,
"fsum_mv": 0.0,
"exact_supplied_binary_sum_mv": "0",
"rounded_rational_reference_mv": 0.0,
"fsum_matches_rounded_reference_here": true,
"absolute_fsum_error_exact_mv": "0",
"absolute_loop_error_exact_mv": "0",
"relative_fsum_error_exact": null,
"relative_loop_error_exact": null
},
"independent_finite_family_ONE_group": {
"alphabet_mv": [
-9007199254740992.0,
-1.0,
-0.5,
0.0,
0.5,
1.0,
9007199254740992.0
],
"lengths": [
0,
1,
2,
3,
4
],
"cases": 2801,
"check_groups_counted": 1,
"rounded_reference_mismatches": 0,
"nonzero_exact_output_rounding_errors": 1082,
"largest_absolute_fsum_error_exact_mv": "2"
},
"unique_check_groups": 14,
"checks": {
"manual_cancellation_retains_one_millivolt": true,
"six_selected_orders_expose_explicit_loop_sensitivity": true,
"empty_frame_has_zero_without_relative_division": true,
"singleton_preserves_the_supplied_binary_value": true,
"dyadic_hand_total_and_this_unit_conversion_are_exact": true,
"nonempty_exact_zero_reports_absolute_error_only": true,
"lost_input_distinction_is_not_recovered_by_summation": true,
"decimal_intent_and_binary_reference_are_different_targets": true,
"sign_reflection_preserves_this_selected_reference_and_sum": true,
"balanced_large_pair_keeps_the_selected_small_total": true,
"declared_container_and_count_boundary_are_enforced": true,
"strict_builtin_float_policy_rejects_silent_coercions": true,
"finite_magnitude_cap_includes_endpoints_and_small_values": true,
"all_2801_small_frames_match_independent_rounding_and_fresh_state": true
},
"limits": [
"Observed finite fixtures, not a universal correct-rounding proof or cross-version output guarantee.",
"The baseline is explicit +=, not Python's current compensated builtin sum.",
"Exact reference targets already supplied binary floats, not intended decimals or sensor truth.",
"No physical measurement, device range, CI service, benchmark or timing was tested."
]
}
۴. خطای جمع با خطای ورودی فرق دارد

برای نمونهٔ ۰٫۱، مرجعِ واقعیِ float کسری با مخرجِ توانی از دو است و با یکدهم برابر نیست. سه بار جمع کردنِ همان ورودی، هدفی اندکی متفاوت از سهدهمِ اعشاری دارد. حتی اگر fsum نزدیکترین خروجی به جمعِ دقیقِ ورودیها را بدهد، الزاماً به عددی که از متنِ اعشاری انتظار داشتیم نمیرسیم. تغییرِ هدفِ مرجع با Fraction(str(x))، این تفاوت را حل نمیکند؛ مسئلهٔ دیگری میسازد.
در شاهدِ جدا، دو عددِ صحیحِ دو به توان پنجاهوچهار و یک واحد بیشتر، پیش از ورود به رابط به float تبدیل میشوند. اگر آن دو به یک مقدار تبدیل شده باشند، تفاضلِ دادههای دریافتشده صفر است؛ جمعِ بهتر نمیتواند اختلافِ صحیحِ قبلی را بازسازی کند. تبدیل عمداً بیرون از رابط انجام میشود تا مرزِ مسئولیتِ تابع مخفی نماند.
درسِ واریانسِ جریانیِ ولتاژ مسئلهٔ دیگری داشت: فرمولِ مرکزی در برابر تفریقِ گشتاورهای بزرگ، با ضربها و مخرجهای متفاوت. اینجا فقط جمعِ مؤلفههای آماده را میسنجیم. درسِ ضریبِ فیلتر و expm1 نیز نشان میداد که اصلاحِ یک مرحله، همهٔ مسیرِ عددی را اصلاح نمیکند؛ در برنامهٔ حاضر نه ضریبِ نمایی میسازیم و نه وضعیتِ فیلتر را بهروز میکنیم.
این مطلب آموزشِ پایه است. جستوجوی منابعِ رسمی در بازهٔ هفتروزه و سپس سیروزه انجام شد، اما تازگیِ خزش یا ساختِ مستندات را تاریخِ انتشارِ روش نمینامیم. منابعِ فنی در ۲۸ مهر ۱۴۰۵ خوانده شدهاند؛ نسخهٔ مستندات، نسخهٔ اجرای برنامه و تاریخِ دسترسی در رسیدها جدا هستند و تاریخِ انتشارِ دقیقِ مدخلها نامعلوم است.
۵. چهار پرسش پیش از ثبتِ نتیجه

پرسش اول: چرا شش ترتیبِ همان سه عدد ارزشِ بررسی دارند؟ پاسخ: ورودیِ گویای دقیق در همهٔ ترتیبها همان مجموعِ یک میلیولت را دارد، ولی مسیرِ جمعِ پیاپی تغییر میکند. خروجیِ ترتیبها در گزارش ثبت میشود. توافقِ fsum در همین شش حالت، گواهِ همین شاهدهاست و اثباتِ استقلال از ترتیب برای هر داده و هر ساختِ کتابخانه نیست.
پرسش دوم: آیا موفقیتِ مرجعِ دودویی یعنی سه مقدارِ ۰٫۱ دقیقاً سهدهم شدهاند؟ پاسخ: خیر. مرجع همان سه float را جمع میکند، نه سه کسرِ یکدهم. اگر قرارداد واقعاً بر اعدادِ اعشاریِ دقیق بنا شده باشد، باید نمایشِ ورودی و روشِ محاسبه از ابتدا با آن هدف سازگار شود؛ تبدیلِ خودکار در انتهای گزارش، خطای قبلی را درمان نمیکند.
پرسش سوم: چرا برای مجموعِ دقیقِ صفر، خطای نسبی None است؟ پاسخ: مخرجِ نسبتِ خطا صفر میشود و نسبت تعریف نشده است. خطای مطلق هنوز قابل محاسبه و واحدش میلیولت است. صفر نوشتن به جای نسبتِ تعریفنشده، میتواند گزارشی نادرست از معیار بسازد؛ صفرِ مجموع نیز بینویز بودن یا بیاریب بودنِ سامانه را نشان نمیدهد.
پرسش چهارم: تبدیلِ واحد چه مرجعی میخواهد؟ پاسخ: در شاهدِ نیم، یکچهارم و منفیِ یکهشتم میلیولت، جمع پنجهشتم و پس از تبدیلِ دقیقِ همین شاهد، ۶۲۵ میکروولت است. اما ضربِ هر ورودیِ عمومی در هزار ممکن است گرد شود. آزمونِ واحد باید ورودیِ تبدیلشده و هدفش را ثبت کند؛ نتیجهٔ این شاهد، تضمینِ برابریِ تمامِ مسیرهای تبدیلِ واحد نیست.
منابع و مطالعهٔ بیشتر
- Python3.14؛ قرارداد و محدودیتِ math.fsum
- Python3.14؛ نمایشِ ممیزشناور و خطای جمع
- Python3.14؛ Fraction و مقدارِ واقعیِ float
بررسی منابع: ۲۸ مهر ۱۴۰۵. تحلیلها و پیشنهادهای اجرایی، جمعبندی نویسندهاند.

