فهرست مقاله
فرض کنید آزمونِ عددیِ یک برنامه دیروز موفق بوده، ولی امروز همان ورودی به خروجی دیگری میرسد. پیش از تغییرِ فرمول، باید ورودیِ پنهانِ محاسبه را هم ببینیم: دقت و قاعدهٔ گردکردنِ محیطِ Decimal. دقیق ذخیره شدنِ یک مقدارِ دهدهی، به معنیِ دقیق ماندنِ تمامِ محاسباتِ بعدی نیست. این درس یک نمونهٔ کوچک و قابل بازبینی برای ثبتِ چنین قراردادی میسازد.
دادهها ساختگیاند و هیچ حسگر یا سرویسِ یکپارچهسازی مداوم اجرا نمیشود. هدف، ساختنِ یک مصنوعِ آزمونِ عددی است که بتوان کد، ورودی، محیط و پاسخِ مرجعش را کنار هم خواند. از نتیجه، دقتِ فیزیکیِ کالیبراسیون، سرعتِ محاسبه یا قابلیتِ اطمینانِ سامانه را استنتاج نمیکنیم. تصاویر نیز فقط مفهوم را نشان میدهند.
۱. مقدارِ دهدهی دقیق است، محاسبه هنوز قرارداد میخواهد

مدلِ تألیفی، شمارِ صحیح را در ضریب ضرب میکند و یک جابهجایی به آن میافزاید. ضریب با واحدِ میلیولت بر شمار و جابهجایی با واحدِ میلیولت تعریف شدهاند؛ نتیجه نیز میلیولت است. دو ورودیِ صحیحِ gain_milli و offset_milli، هزار برابرِ مقدارِ این دو پارامترند. بنابراین ضریبِ ۱۰۰۵ به معنیِ ۱٫۰۰۵ میلیولت بر شمار است؛ نامِ متغیر، قراردادِ مقیاس را عوض نمیکند.
رابط فقط چهار عددِ صحیحِ داخلی را میپذیرد: شمار از صفر تا ۹۹۹، ضریبِ مقیاسشده از صفر تا ۲۰۰۰، جابهجاییِ مقیاسشده از منفی ۲۰۰۰ تا مثبت ۲۰۰۰ و دقتِ کاری از سه تا دوازده رقمِ معنیدار. بولی، float، رشته و زیرکلاسِ int رد میشوند. این سقفها محدودیتِ آموزشیِ کد هستند؛ مشخصاتِ مبدل یا محدودهٔ واقعیِ ابزار نیستند.
طبقِ مستنداتِ کاملِ Decimal و context، ساختنِ Decimal از متنِ معتبر، رقمها را مستقل از دقتِ context نگه میدارد. به همین دلیل، رشتهٔ ۱٫۰۰۵ حتی در محیطِ سهرقمی همان مقدار را میسازد. اما ضرب و جمعِ بعدی، دقتِ محیط را رعایت میکنند. کد از float عبور نمیکند و ضریبها را با متنِ صحیح و توانِ منفیِ سه میسازد.
۲. گردکردنِ میانی را quantize برنمیگرداند

شاهدِ دستی را پیش از مرجعِ ماشینی حساب میکنیم: ۱۲۳ ضربدر ۱٫۰۰۵ برابرِ ۱۲۳٫۶۱۵ است؛ با افزودنِ ۰٫۰۵، جمعِ دقیق ۱۲۳٫۶۶۵ میشود. اگر فقط در پایان تا صدم و با قاعدهٔ نزدیکترینِ زوج گرد کنیم، پاسخ ۱۲۳٫۶۶ است. اینجا رقمِ صدم شش است و در تساوی، زوج باقی میماند.
در دقتِ کاریِ چهار، محصول ابتدا به ۱۲۳٫۶ میرسد. جمعِ بعدی، ۱۲۳٫۶۵ را دوباره تا چهار رقم گرد میکند و به ۱۲۳٫۶ بازمیگردد. سپس context جداگانهٔ شانزدهرقمی، خروجی را با نمای منفیِ دو قالب میدهد: ۱۲۳٫۶۰. افزایشِ دقتِ همین مرحلهٔ نهایی، رقمهای حذفشدهٔ ضرب و جمع را بازسازی نمیکند. دقتِ کاریِ دوازده در همین شاهد، محاسبهٔ میانی را دقیق نگه میدارد و نتیجهٔ نهایی ۱۲۳٫۶۶ میشود.
متدِ quantize، نمای نتیجه را از عملوندِ دوم میگیرد؛ تعیینِ صدم با تعیینِ تعدادِ رقمِ معنیدار یکی نیست. اگر بخواهیم ۱۲۳٫۶ را در context چهاررقمی به صدم تبدیل کنیم، ضریبِ نتیجه به رقمهای بیشتری نیاز دارد و InvalidOperation رخ میدهد. برنامه این استثنای مورد انتظار را میگیرد و پرچمش را ثبت میکند؛ آن را شکستِ اجرای درس معرفی نمیکند.
درسِ آزمونِ کوانتیزهساز با مرجعِ گویا مسئلهٔ دیگری داشت: انتخابِ سطح، اشباع و حدِ خطا در یک مدلِ صحیح. اینجا سطحهای یک مبدل را نمیشماریم؛ محیطِ محاسبهٔ دهدهی و ترتیبِ گردشدنِ میانی موضوعِ آزمون است.
۳. context محلی؛ مرزِ دقت، پرچم و استثنا

تابعِ سازندهٔ context همهٔ فیلدها، از دقت و گردکردن تا کرانِ توان، پرچمها و تلهها را صریح میدهد. اتکا به پارامترهای حذفشده میتواند وابستگی به DefaultContext قابل تغییر را پنهان کند. هر فراخوانی، context تازهای میسازد و localcontext یک کپی از آن را نصب میکند؛ در خروجِ عادی و هنگامِ خروجِ استثنا، محیطِ قبلی بازگردانده میشود.
در شرایطِ استثناییِ General Decimal Arithmetic، Rounded و Inexact یک معنا ندارند. حذفِ رقمهای صفر هم میتواند Rounded باشد؛ Inexact از حذفِ بخشِ غیرصفر خبر میدهد. شاهدِ ۱٫۲۳۰۰ به ۱٫۲۳، اولی را فعال میکند و دومی را نه. گزارشِ پرچم نیز با تله فرق دارد: فعال بودنِ پرچم بهتنهایی به معنیِ پرتابِ استثنا نیست.
پرچمها چسبندهاند. پس از تقسیمِ غیردقیق، یک جمعِ دقیق آنها را خاموش نمیکند؛ برای شروعِ بازهٔ مشاهدهٔ تازه، clear_flags لازم است. کد پرچمِ مرحلهٔ کاری و quantize نهایی را جدا ثبت میکند. در شاهدِ کمدقت، قالبدهیِ نهایی میتواند دقیق باشد، در حالی که اطلاعات قبلاً از دست رفته است. از «Inexact نهایی خاموش است» نباید «کلِ محاسبه دقیق بود» نتیجه گرفت.
۴. کدِ کامل با مرجعِ صحیحِ گردکردن

فایلِ کامل را با نامِ decimal_context_quantize.py ذخیره و با Python اجرا کنید. وابستگی فقط کتابخانهٔ استاندارد است و نسخهٔ واقعی در خروجی ثبت میشود. این کد ابزارِ اندازهگیریِ حافظه، زمانسنج یا اتصالِ خطِ تحویل نیست؛ مرزِ قابل تکرارِ یک محاسبهٔ همگام را بررسی میکند.
"""Bounded synthetic decimal calibration; explicit numerical context, not a CI run."""
from decimal import (Decimal, Context, localcontext, getcontext, ROUND_HALF_EVEN,
ROUND_DOWN, InvalidOperation, DivisionByZero, Overflow,
Inexact, Rounded)
from fractions import Fraction as F
from itertools import product
import json
import platform
import sys
def arithmetic_context(precision):
# Every constructor field is explicit: do not inherit mutable DefaultContext.
return Context(prec=precision, rounding=ROUND_HALF_EVEN, Emin=-99, Emax=99,
capitals=1, clamp=0, flags=[],
traps=[InvalidOperation, DivisionByZero, Overflow])
def signal_report(context):
return {"Rounded": bool(context.flags[Rounded]),
"Inexact": bool(context.flags[Inexact])}
def calibrate(count, gain_milli, offset_milli, precision):
"""Return synthetic mV: count * (gain_milli / 1000) + offset_milli / 1000."""
arguments = (count, gain_milli, offset_milli, precision)
if any(type(x) is not int for x in arguments):
raise TypeError("Four exact builtin integers required; no coercion")
if not (0 <= count <= 999 and 0 <= gain_milli <= 2000
and -2000 <= offset_milli <= 2000 and 3 <= precision <= 12):
raise ValueError("Outside the authored count/coefficient/precision caps")
# String constructors are exact even when the installed precision is smaller.
gain = Decimal(str(gain_milli) + "e-3")
offset = Decimal(str(offset_milli) + "e-3")
with localcontext(arithmetic_context(precision)) as working:
intermediate = Decimal(count) * gain
total = intermediate + offset
working_flags = signal_report(working)
# This is a separate final-formatting context, not a repair of earlier loss.
with localcontext(arithmetic_context(16)) as final:
rounded = total.quantize(Decimal("0.01"))
final_flags = signal_report(final)
return {"input": list(arguments), "unit": "synthetic mV",
"product_mv": str(intermediate), "sum_before_quantize_mv": str(total),
"result_mv": str(rounded), "working_flags": working_flags,
"final_quantize_flags": final_flags}
def nearest_even_integer(value):
# Independent integer quotient/remainder arithmetic, including negative ties.
sign = -1 if value < 0 else 1
numerator = abs(value.numerator)
quotient, remainder = divmod(numerator, value.denominator)
twice = 2 * remainder
if twice > value.denominator or (twice == value.denominator and quotient % 2):
quotient += 1
return sign * quotient
def ten_power(exponent):
return F(10 ** exponent) if exponent >= 0 else F(1, 10 ** (-exponent))
def rational_significant_round(value, precision):
# Find the decimal order with exact comparisons, not Decimal or float logs.
if value == 0:
return F(0)
magnitude = abs(value)
order = 0
while magnitude < ten_power(order):
order -= 1
while magnitude >= ten_power(order + 1):
order += 1
step = ten_power(order - precision + 1)
return nearest_even_integer(value / step) * step
def rational_reference(count, gain_milli, offset_milli, precision):
# Does not call calibrate, its validator, Decimal, Context, or quantize.
exact_product = F(count * gain_milli, 1000)
working_product = rational_significant_round(exact_product, precision)
working_total = rational_significant_round(
working_product + F(offset_milli, 1000), precision)
cents = F(nearest_even_integer(working_total * 100), 100)
direct_cents = F(nearest_even_integer(
(exact_product + F(offset_milli, 1000)) * 100), 100)
return working_product, working_total, cents, direct_cents
def context_snapshot(context):
return (context.prec, context.rounding, context.Emin, context.Emax,
context.capitals, context.clamp,
tuple(sorted((s.__name__, bool(v)) for s, v in context.flags.items())),
tuple(sorted((s.__name__, bool(v)) for s, v in context.traps.items())))
def rejects(call, expected):
try:
call()
except expected:
return True
return False
def finite_family():
cases = mismatches = high_precision_direct_mismatches = 0
direct_disagreements = 0
for arguments in product((0, 1, 7, 123, 500, 999), (0, 1, 125, 1005, 2000),
(-2000, -50, 0, 50, 1225), (3, 4, 5, 8, 12)):
actual = calibrate(*arguments)
reference = rational_reference(*arguments)
# Fraction parses the returned finite decimal strings exactly.
observed = tuple(F(actual[key]) for key in
("product_mv", "sum_before_quantize_mv", "result_mv"))
cases += 1
mismatches += observed != reference[:3]
direct_disagreements += observed[2] != reference[3]
if arguments[3] == 12:
high_precision_direct_mismatches += observed[2] != reference[3]
return {"cases": cases, "check_groups_counted": 1,
"working_pipeline_reference_mismatches": mismatches,
"results_different_from_exact_then_final_round": direct_disagreements,
"precision12_direct_reference_mismatches": high_precision_direct_mismatches}
def run_example():
initial = context_snapshot(getcontext())
low = calibrate(123, 1005, 50, 4)
high = calibrate(123, 1005, 50, 12)
reference = rational_reference(123, 1005, 50, 4)
with localcontext(arithmetic_context(3)):
exact_constructor = str(Decimal("1.005")) == "1.005"
with localcontext(arithmetic_context(16)):
unrepaired = str(Decimal(low["result_mv"]).quantize(Decimal("0.01")))
with localcontext(arithmetic_context(4)) as insufficient:
invalid_trapped = rejects(
lambda: Decimal("123.6").quantize(Decimal("0.01")), InvalidOperation)
invalid_flag = bool(insufficient.flags[InvalidOperation])
with localcontext(arithmetic_context(12)):
ties = [str(Decimal(x).quantize(Decimal("0.01")))
for x in ("1.225", "1.235", "-1.225", "-1.235")]
with localcontext(arithmetic_context(12)) as zeros:
zeros_result = str(Decimal("1.2300").quantize(Decimal("0.01")))
zeros_flags = signal_report(zeros)
with localcontext(arithmetic_context(3)) as sticky:
Decimal(1) / Decimal(7)
after_inexact = signal_report(sticky)
Decimal(1) + Decimal(1)
after_exact = signal_report(sticky)
sticky.clear_flags()
after_clear = signal_report(sticky)
with localcontext(arithmetic_context(9)) as parent:
before_parent = context_snapshot(parent)
with localcontext() as child:
child.prec = 3
Decimal(1) / Decimal(7)
normal_restore = getcontext() is parent and context_snapshot(parent) == before_parent
marker = RuntimeError("authored restoration sentinel")
caught_same = False
try:
with localcontext(arithmetic_context(3)):
Decimal(1) / Decimal(7)
raise marker
except RuntimeError as error:
caught_same = error is marker
exception_restore = (caught_same and getcontext() is parent
and context_snapshot(parent) == before_parent)
with localcontext(arithmetic_context(5)) as ambient:
ambient.rounding = ROUND_DOWN
ambient.traps[InvalidOperation] = False
Decimal(1) / Decimal(7)
before_ambient = context_snapshot(ambient)
isolated = calibrate(123, 1005, 50, 4)
ambient_unchanged = context_snapshot(ambient) == before_ambient
class IntChild(int):
pass
invalids = []
for index in range(4):
for value in (True, 4.0, "4", IntChild(4)):
values = [123, 1005, 50, 4]
values[index] = value
invalids.append((values, TypeError))
for values in ((-1, 1005, 0, 4), (1000, 1005, 0, 4), (1, -1, 0, 4),
(1, 2001, 0, 4), (1, 1, -2001, 4), (1, 1, 2001, 4),
(1, 1, 0, 2), (1, 1, 0, 13)):
invalids.append((values, ValueError))
family = finite_family()
first = calibrate(0, 0, -2000, 12)
first["input"][0] = 999
first["working_flags"]["Inexact"] = True
fresh = calibrate(0, 0, -2000, 12)
endpoint = calibrate(999, 2000, 2000, 12)
checks = {
"manual_low_precision_product_sum_and_final_cents":
low["product_mv"] == "123.6" and low["sum_before_quantize_mv"] == "123.6"
and low["result_mv"] == "123.60" and reference[3] == F(6183, 50),
"precision12_preserves_selected_exact_arithmetic_before_final_tie":
F(high["product_mv"]) == F(24723, 200)
and F(high["sum_before_quantize_mv"]) == F(24733, 200)
and high["result_mv"] == "123.66"
and high["working_flags"] == {"Rounded": False, "Inexact": False},
"constructor_stores_digits_independently_of_context_precision": exact_constructor,
"later_higher_precision_quantize_does_not_repair_intermediate_loss":
unrepaired == "123.60" and unrepaired != high["result_mv"],
"insufficient_quantize_precision_is_a_trapped_invalid_operation":
invalid_trapped and invalid_flag,
"positive_and_negative_half_even_ties_have_manual_answers":
ties == ["1.22", "1.24", "-1.22", "-1.24"],
"discarding_only_trailing_zeros_is_rounded_but_not_inexact":
zeros_result == "1.23" and zeros_flags == {"Rounded": True, "Inexact": False},
"phase_flags_separate_working_loss_from_final_formatting":
low["working_flags"] == {"Rounded": True, "Inexact": True}
and low["final_quantize_flags"] == {"Rounded": False, "Inexact": False}
and high["final_quantize_flags"] == {"Rounded": True, "Inexact": True},
"flags_stick_through_exact_operation_until_explicitly_cleared":
after_inexact == after_exact == {"Rounded": True, "Inexact": True}
and after_clear == {"Rounded": False, "Inexact": False},
"localcontext_is_a_copy_and_restores_parent_on_normal_exit": normal_restore,
"localcontext_restores_parent_when_same_body_exception_escapes": exception_restore,
"explicit_context_ignores_outer_precision_rounding_traps_and_sticky_flags":
isolated == low and ambient_unchanged,
"independent_integer_fraction_family_is_ONE_group":
family["cases"] == 750
and family["working_pipeline_reference_mismatches"] == 0
and family["precision12_direct_reference_mismatches"] == 0,
"strict_full_validation_resource_endpoints_and_fresh_results":
len(invalids) == 24
and all(rejects(lambda a=a: calibrate(*a), e) for a, e in invalids)
and fresh["input"] == [0, 0, -2000, 12] and fresh["result_mv"] == "-2.00"
and fresh["working_flags"] == {"Rounded": False, "Inexact": False}
and endpoint["result_mv"] == "2000.00"
and context_snapshot(getcontext()) == initial,
}
assert len(checks) == 14 and all(type(v) is bool for v in checks.values())
assert all(checks.values()), checks
return {"python": platform.python_version(), "implementation": platform.python_implementation(),
"dependencies": "standard library only", "units": "synthetic millivolts",
"formula": "count * (gain_milli / 1000) + offset_milli / 1000",
"own_bounds": {"exact_integer_count": [0, 999], "gain_milli": [0, 2000],
"offset_milli": [-2000, 2000], "working_precision": [3, 12],
"final_quantize_precision": 16, "final_exponent": -2},
"manual_precision4": low, "manual_precision12": high,
"exact_manual_product_mv": "123.615", "exact_manual_sum_mv": "123.665",
"exact_then_final_half_even_mv": "123.66", "higher_precision_requantize": unrepaired,
"manual_half_even_ties": ties, "rounded_only": {"result": zeros_result, "flags": zeros_flags},
"expected_InvalidOperation": {"caught": invalid_trapped, "flag": invalid_flag},
"sticky_flags": {"after_inexact": after_inexact, "after_exact": after_exact,
"after_clear": after_clear},
"independent_finite_family_ONE_group": family, "invalid_cases_ONE_group": len(invalids),
"unique_check_groups": len(checks), "checks": checks,
"limits": ["Authored bounded synthetic arithmetic, not hardware calibration or physical accuracy.",
"A numerical regression artifact; no CI platform, service, timing or benchmark ran.",
"Finite oracle comparisons are not an exhaustive proof over every valid input.",
"Context isolation is local to these synchronous calls, not a concurrency claim.",
"A higher precision final quantize cannot reconstruct discarded intermediate digits."]}
if __name__ == "__main__":
sys.stdout.buffer.write((json.dumps(run_example(), ensure_ascii=True, indent=2) + "\n").encode("utf-8"))
مرجع، صورت و مخرجِ صحیح را نگه میدارد. با مقایسهٔ دقیق با توانهای ده، مرتبهٔ عدد و گامِ رقمِ معنیدار را پیدا میکند؛ سپس با خارجقسمت و باقیمانده، تساویِ زوج را حل میکند. مرجع نه Decimal و quantize را فراخوانی میکند و نه تابعِ اصلی را. محصول، جمع و خروجیِ گردشده جدا مقایسه میشوند تا توافقِ تصادفیِ پاسخِ نهایی، اختلافِ مسیر را نپوشاند.
خانوادهٔ محدود، شش شمار، پنج ضریب، پنج جابهجایی و پنج دقت را ترکیب میکند: ۷۵۰ حالت و فقط یک گروهِ آزمون. چهارده گروهِ نامدار، همین خانواده و شاهدهای دستی، تساویِ مثبت و منفی، پرچمِ صفرهای حذفشده، چسبندگی، بازگرداندنِ context در دو مسیر، استقلال از محیطِ بیرونی، ورودیهای نامعتبر و تازگیِ خروجی را در بر میگیرند. شمارِ حالتها، شمارِ گروهها یا اجرای نویسنده نیست.
{
"python": "3.14.5",
"implementation": "CPython",
"dependencies": "standard library only",
"units": "synthetic millivolts",
"formula": "count * (gain_milli / 1000) + offset_milli / 1000",
"own_bounds": {
"exact_integer_count": [
0,
999
],
"gain_milli": [
0,
2000
],
"offset_milli": [
-2000,
2000
],
"working_precision": [
3,
12
],
"final_quantize_precision": 16,
"final_exponent": -2
},
"manual_precision4": {
"input": [
123,
1005,
50,
4
],
"unit": "synthetic mV",
"product_mv": "123.6",
"sum_before_quantize_mv": "123.6",
"result_mv": "123.60",
"working_flags": {
"Rounded": true,
"Inexact": true
},
"final_quantize_flags": {
"Rounded": false,
"Inexact": false
}
},
"manual_precision12": {
"input": [
123,
1005,
50,
12
],
"unit": "synthetic mV",
"product_mv": "123.615",
"sum_before_quantize_mv": "123.665",
"result_mv": "123.66",
"working_flags": {
"Rounded": false,
"Inexact": false
},
"final_quantize_flags": {
"Rounded": true,
"Inexact": true
}
},
"exact_manual_product_mv": "123.615",
"exact_manual_sum_mv": "123.665",
"exact_then_final_half_even_mv": "123.66",
"higher_precision_requantize": "123.60",
"manual_half_even_ties": [
"1.22",
"1.24",
"-1.22",
"-1.24"
],
"rounded_only": {
"result": "1.23",
"flags": {
"Rounded": true,
"Inexact": false
}
},
"expected_InvalidOperation": {
"caught": true,
"flag": true
},
"sticky_flags": {
"after_inexact": {
"Rounded": true,
"Inexact": true
},
"after_exact": {
"Rounded": true,
"Inexact": true
},
"after_clear": {
"Rounded": false,
"Inexact": false
}
},
"independent_finite_family_ONE_group": {
"cases": 750,
"check_groups_counted": 1,
"working_pipeline_reference_mismatches": 0,
"results_different_from_exact_then_final_round": 84,
"precision12_direct_reference_mismatches": 0
},
"invalid_cases_ONE_group": 24,
"unique_check_groups": 14,
"checks": {
"manual_low_precision_product_sum_and_final_cents": true,
"precision12_preserves_selected_exact_arithmetic_before_final_tie": true,
"constructor_stores_digits_independently_of_context_precision": true,
"later_higher_precision_quantize_does_not_repair_intermediate_loss": true,
"insufficient_quantize_precision_is_a_trapped_invalid_operation": true,
"positive_and_negative_half_even_ties_have_manual_answers": true,
"discarding_only_trailing_zeros_is_rounded_but_not_inexact": true,
"phase_flags_separate_working_loss_from_final_formatting": true,
"flags_stick_through_exact_operation_until_explicitly_cleared": true,
"localcontext_is_a_copy_and_restores_parent_on_normal_exit": true,
"localcontext_restores_parent_when_same_body_exception_escapes": true,
"explicit_context_ignores_outer_precision_rounding_traps_and_sticky_flags": true,
"independent_integer_fraction_family_is_ONE_group": true,
"strict_full_validation_resource_endpoints_and_fresh_results": true
},
"limits": [
"Authored bounded synthetic arithmetic, not hardware calibration or physical accuracy.",
"A numerical regression artifact; no CI platform, service, timing or benchmark ran.",
"Finite oracle comparisons are not an exhaustive proof over every valid input.",
"Context isolation is local to these synchronous calls, not a concurrency claim.",
"A higher precision final quantize cannot reconstruct discarded intermediate digits."
]
}
خروجیِ کاملِ ثبتشده را کنار کد بخوانید. توافق با مرجعِ گویای مسیرِ کمدقت، درست پیادهسازی شدنِ همان مسیر را بررسی میکند؛ الزاماً برابر بودن با محاسبهٔ دقیق و یک گردکردنِ نهایی را نمیگوید. گزارش این دو هدف را جدا میشمارد. مشاهدهٔ خانوادهٔ محدود، اثباتِ جامع برای تمامِ ورودیهای مجاز یا تمامِ نسخههای پیادهسازی نیست.
در بازبینیِ جمعِ fsum و ورودیِ واقعی، مرجعِ دقیق به floatهای دریافتشده مربوط بود و مسئلهٔ نمایشِ دودویی را جدا میکرد. اینجا ورودیها دهدهیِ دقیقاند، ولی دقتِ عملیاتِ میانی محدود است. تغییرِ نمایشِ ورودی بهتنهایی، قراردادِ محاسبه را کامل نمیکند.
۵. چهار پرسش برای آزمونِ تکرارپذیر

پرسش اول: چرا context شانزدهرقمیِ پایان، پاسخِ کمدقت را درست نمیکند؟ پاسخ: ورودیِ مرحلهٔ پایانی قبلاً ۱۲۳٫۶ شده است. quantize فقط آن مقدار را به ۱۲۳٫۶۰ با نمای صدم میرساند؛ رقمهای محصولِ دقیق دیگر در آن نیستند. باید دقتِ کاری و هدفِ مرجع را پیش از محاسبه تعیین کرد.
پرسش دوم: آیا Rounded روشن همیشه خطای مقداری دارد؟ پاسخ: خیر. تبدیلِ ۱٫۲۳۰۰ به ۱٫۲۳ فقط صفرهای انتهایی را حذف میکند؛ مقدار همان است، Rounded روشن و Inexact خاموش است. برای تحلیل، این دو پرچم و مرحلهای که در آن مشاهده شدهاند را جدا ثبت میکنیم.
پرسش سوم: آیا clear_flags دقتِ قبلی را بازمیگرداند؟ پاسخ: خیر. فقط وضعیتِ گزارشِ شرطها را پاک میکند. نه رقمِ ازدسترفته برمیگردد و نه قاعدهٔ گردکردن عوض میشود. پاککردنِ پرچم، آغازِ بازهٔ مشاهدهٔ تازه است؛ درمانِ محاسبهٔ پیشین نیست.
پرسش چهارم: برای انتقالِ این آزمون به پروژه چه چیزی باید ثبت شود؟ پاسخ: فرمول، واحد، مقیاسِ پارامترها، سقفِ ورودی، همهٔ تنظیماتِ context، هدفِ مرجع و خروجیِ واقعی را ثبت کنید. آزمونِ حاضر روی سرویسِ CI اجرا نشده است؛ ادغام در آن سامانه، مدیریتِ خطا و سیاستِ پذیرشِ عددی، کارِ جداگانه میخواهد.
این آموزشِ پایه بر خواندنِ منابع در ۲۹ مهر ۱۴۰۵ تکیه دارد. جستوجوی رسمیِ هفتروزه و سپس سیروزه، خبرِ مناسبی برای این درس نداد. مستنداتِ Python نسخهٔ ۳٫۱۴٫۸ و سندِ حسابِ دهدهی نسخهٔ ۱٫۷۰ با تاریخِ صریحِ هفتمِ آوریلِ ۲۰۰۹ هستند؛ تاریخِ دسترسی، تاریخِ انتشارِ اولیه نیست. نسخهٔ اجرای واقعی جدا در خروجی میآید.
منابع و مطالعهٔ بیشتر
- Python3.14؛ Decimal، quantize و محیط محاسبه
- General Decimal Arithmetic؛ شرایط استثنایی، Rounded و Inexact
بررسی منابع: ۲۹ مهر ۱۴۰۵. تحلیلها و پیشنهادهای اجرایی، جمعبندی نویسندهاند.

