提取工具函数

This commit is contained in:
SOR
2026-06-21 11:05:42 +08:00
parent c59105ef43
commit 1c48f91be5
13 changed files with 259 additions and 444 deletions
+7 -66
View File
@@ -1,5 +1,11 @@
import math import math
from decimal import Decimal, ROUND_HALF_EVEN from decimal import Decimal, ROUND_HALF_EVEN
from utils import (
scientific_round,
calculate_stats,
input_decimal,
input_int
)
""" """
Wheatstone Bridge Resistance Measurement Calculator Wheatstone Bridge Resistance Measurement Calculator
@@ -56,71 +62,6 @@ Wheatstone Bridge Resistance Measurement Calculator
Rx 末位与不确定度对齐 Rx 末位与不确定度对齐
""" """
def scientific_round(value, uncertainty):
"""
按照"四舍六入五凑偶"修约:
1. 不确定度 u 保留一位有效数字
2. 平均值末位与 u 对齐
"""
if uncertainty <= 0:
return str(value), "0"
u_float = float(uncertainty)
first_digit_pos = math.floor(math.log10(u_float))
prec = Decimal('1e' + str(first_digit_pos))
u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN)
val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN)
return val_final, u_final
def calculate_stats(data_list, delta_inst):
"""
计算一组数据的平均值、A类、B类及合成不确定度
u(X) = sqrt( u_A^2 + u_B^2 )
u_A = s / sqrt(n)
u_B = Δ_inst / sqrt(3)
"""
n = len(data_list)
mean = sum(data_list) / Decimal(n)
if n > 1:
variance = sum((x - mean)**2 for x in data_list) / Decimal(n - 1)
s = Decimal(str(math.sqrt(float(variance))))
u_a = s / Decimal(str(math.sqrt(n)))
else:
u_a = Decimal("0")
u_b = Decimal(delta_inst) / Decimal(str(math.sqrt(3)))
u_combined = Decimal(str(math.sqrt(float(u_a**2 + u_b**2))))
return mean, u_a, u_b, u_combined
def input_decimal(prompt, default=None):
"""输入一个 Decimal 值,支持默认值"""
while True:
try:
suffix = f" (默认 {default})" if default else ""
val = input(f"{prompt}{suffix}: ").strip()
if not val and default is not None:
return Decimal(str(default))
if not val:
continue
return Decimal(val)
except Exception:
print("输入无效,请输入数字。")
def input_int(prompt, default=None):
"""输入一个整数,支持默认值"""
while True:
try:
suffix = f" (默认 {default})" if default else ""
val = input(f"{prompt}{suffix}: ").strip()
if not val and default is not None:
return default
return int(val)
except Exception:
print("输入无效,请输入整数。")
def measure_one_ratio(ratio_label, C_value, R_nominal, n_measurements): def measure_one_ratio(ratio_label, C_value, R_nominal, n_measurements):
""" """
@@ -174,7 +115,7 @@ def measure_one_ratio(ratio_label, C_value, R_nominal, n_measurements):
delta_inst = R0_mean / S delta_inst = R0_mean / S
# --- 不确定度分析 --- # --- 不确定度分析 ---
R0_mean_final, u_a_R0, u_b_R0, u_R0 = calculate_stats(R0_data, str(delta_inst)) R0_mean_final, u_R0, u_a_R0, u_b_R0, _ = calculate_stats(R0_data, str(delta_inst))
# Rx = C * R0 # Rx = C * R0
Rx_mean = C * R0_mean_final Rx_mean = C * R0_mean_final
+8 -65
View File
@@ -83,68 +83,11 @@ Hall Effect Experiment Data Processor
# 通用工具函数 # 通用工具函数
# ═══════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════
def scientific_round(value, uncertainty): from utils import (
""" scientific_round,
按照"四舍六入五凑偶"修约: linear_regression,
1. 不确定度 u 保留一位有效数字 input_float
2. 结果末位与 u 对齐 )
"""
if uncertainty <= 0:
return str(value), "0"
d_val = Decimal(str(value))
d_unc = Decimal(str(uncertainty))
first_digit_pos = math.floor(math.log10(float(d_unc)))
prec = Decimal('1e' + str(first_digit_pos))
u_final = d_unc.quantize(prec, rounding=ROUND_HALF_EVEN)
val_final = d_val.quantize(prec, rounding=ROUND_HALF_EVEN)
return val_final, u_final
def linear_regression(x_list, y_list):
"""
最小二乘法线性回归: y = k·x + b
返回: 斜率 k, 截距 b, 相关系数 r, 斜率不确定度 u_k
u_k = |k| · √[(1/r² − 1) / (n − 2)]
"""
n = len(x_list)
x_mean = sum(x_list) / n
y_mean = sum(y_list) / n
Lxx = sum((xi - x_mean) ** 2 for xi in x_list)
Lyy = sum((yi - y_mean) ** 2 for yi in y_list)
Lxy = sum((x_list[i] - x_mean) * (y_list[i] - y_mean) for i in range(n))
k = Lxy / Lxx if Lxx != 0 else 0.0
b = y_mean - k * x_mean
denom = math.sqrt(Lxx * Lyy) if Lxx * Lyy > 0 else 1.0
r = Lxy / denom
if abs(r) < 1.0 and n > 2:
u_k = abs(k) * math.sqrt((1.0 / r ** 2 - 1.0) / (n - 2))
else:
u_k = 0.0
return k, b, r, u_k
def input_float(prompt, default=None):
"""输入一个浮点数值,支持默认值"""
while True:
try:
suffix = f" (默认 {default})" if default is not None else ""
val = input(f"{prompt}{suffix}: ").strip()
if not val and default is not None:
return float(default)
if not val:
continue
return float(val)
except Exception:
print(" 输入无效,请输入数字。")
def print_regression(label, k, b, r, u_k, x_unit, y_unit): def print_regression(label, k, b, r, u_k, x_unit, y_unit):
@@ -259,7 +202,7 @@ def main():
print(f"{'─' * 46}") print(f"{'─' * 46}")
# 线性回归: VH(mV) vs Is(mA) —— 使用绝对值 VH # 线性回归: VH(mV) vs Is(mA) —— 使用绝对值 VH
slope_1, intercept_1, r_1, u_slope_1 = linear_regression(Is_1, VH_abs_1) slope_1, intercept_1, r_1, u_slope_1 = map(float, linear_regression(Is_1, VH_abs_1))
print_regression("VH-Is", slope_1, intercept_1, r_1, u_slope_1, "mA", "mV") print_regression("VH-Is", slope_1, intercept_1, r_1, u_slope_1, "mA", "mV")
# 注: 斜率单位 mV/mA = V/A # 注: 斜率单位 mV/mA = V/A
@@ -321,7 +264,7 @@ def main():
print(f"{'─' * 46}") print(f"{'─' * 46}")
# 线性回归: VH(mV) vs Im(mA) # 线性回归: VH(mV) vs Im(mA)
slope_2, intercept_2, r_2, u_slope_2 = linear_regression(Im_2, VH_abs_2) slope_2, intercept_2, r_2, u_slope_2 = map(float, linear_regression(Im_2, VH_abs_2))
print_regression("VH-Im", slope_2, intercept_2, r_2, u_slope_2, "mA", "mV") print_regression("VH-Im", slope_2, intercept_2, r_2, u_slope_2, "mA", "mV")
input("\n 按回车继续...") input("\n 按回车继续...")
@@ -355,7 +298,7 @@ def main():
print(f"{'─' * 36}") print(f"{'─' * 36}")
# 线性回归: Vσ(mV) vs Is(mA) # 线性回归: Vσ(mV) vs Is(mA)
slope_3, intercept_3, r_3, u_slope_3 = linear_regression(Is_3, Vsigma) slope_3, intercept_3, r_3, u_slope_3 = map(float, linear_regression(Is_3, Vsigma))
print_regression("Vσ-Is", slope_3, intercept_3, r_3, u_slope_3, "mA", "mV") print_regression("Vσ-Is", slope_3, intercept_3, r_3, u_slope_3, "mA", "mV")
print(f" 注: 斜率 = Vσ/Is, 单位 mV/mA = V/A = Ω (样品电阻)") print(f" 注: 斜率 = Vσ/Is, 单位 mV/mA = V/A = Ω (样品电阻)")
+1 -29
View File
@@ -8,35 +8,7 @@ Newton's Rings Curvature Radius Calculator (Successive Difference Method)
参考 lenth.py 的高精度与不确定度修约逻辑。 参考 lenth.py 的高精度与不确定度修约逻辑。
""" """
def get_decimal_places(data_strings): from utils import get_decimal_places, scientific_round
"""获取输入数据中小数点后的保留位数,用于自适应展示"""
max_places = 0
for s in data_strings:
if '.' in s:
places = len(s.split('.')[1])
max_places = max(max_places, places)
return max_places
def scientific_round(value, uncertainty):
"""
按照“四舍六入五凑偶”修约:
1. 不确定度 u 保留一位有效数字
2. 平均值末位与 u 对齐
"""
if uncertainty == 0:
return str(value), "0"
u_float = float(uncertainty)
# 找到第一位非零数字的位置
first_digit_pos = math.floor(math.log10(u_float))
prec = Decimal('1e' + str(first_digit_pos))
# 不确定度修约
u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN)
# 平均值修约,保持与不确定度末位对齐
val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN)
return val_final, u_final
def main(): def main():
# --- 1. 参数与常量初始化 --- # --- 1. 参数与常量初始化 ---
+1 -17
View File
@@ -33,23 +33,7 @@ Speed of Sound Calculator (Successive Difference Method)
- 声速 v 的不确定度 (忽略 f 的极小不确定度): u(v) = f * u(λ) - 声速 v 的不确定度 (忽略 f 的极小不确定度): u(v) = f * u(λ)
""" """
def scientific_round(value, uncertainty): from utils import scientific_round
"""
按照“四舍六入五凑偶”修约:
1. 不确定度 u 保留一位有效数字
2. 平均值末位与 u 对齐
"""
if uncertainty == 0:
return str(value), "0"
u_float = float(uncertainty)
first_digit_pos = math.floor(math.log10(u_float))
prec = Decimal('1e' + str(first_digit_pos))
u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN)
val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN)
return val_final, u_final
def calculate_method(method_name, f, k, delta_inst): def calculate_method(method_name, f, k, delta_inst):
print(f"\n========================================") print(f"\n========================================")
+9 -84
View File
@@ -44,87 +44,12 @@ Surface Tension Coefficient Calculator (Pull-off Method)
u_B,X = Δ_inst / sqrt(3) u_B,X = Δ_inst / sqrt(3)
""" """
def scientific_round(value, uncertainty): from utils import (
""" scientific_round,
按照“四舍六入五凑偶”修约: calculate_stats,
1. 不确定度 u 保留一位有效数字 linear_regression,
2. 平均值末位与 u 对齐 input_data_group
""" )
if uncertainty <= 0:
return str(value), "0"
u_float = float(uncertainty)
first_digit_pos = math.floor(math.log10(u_float))
prec = Decimal('1e' + str(first_digit_pos))
u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN)
val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN)
return val_final, u_final
def calculate_stats(data_list, delta_inst):
"""计算一组数据的平均值、A类、B类及合成不确定度"""
n = len(data_list)
mean = sum(data_list) / Decimal(n)
if n > 1:
variance = sum((x - mean)**2 for x in data_list) / Decimal(n - 1)
s = Decimal(str(math.sqrt(float(variance))))
u_a = s / Decimal(str(math.sqrt(n)))
else:
u_a = Decimal("0")
u_b = Decimal(delta_inst) / Decimal(str(math.sqrt(3)))
u_combined = Decimal(str(math.sqrt(float(u_a**2 + u_b**2))))
return mean, u_combined
def linear_regression(x_list, y_list):
"""
对 (x, y) 列表进行线性回归,返回斜率 k, 截距 b, 及斜率的标准不确定度 u_k
"""
n = len(x_list)
x_mean = sum(x_list) / Decimal(n)
y_mean = sum(y_list) / Decimal(n)
L_xx = sum((x - x_mean)**2 for x in x_list)
L_yy = sum((y - y_mean)**2 for y in y_list)
L_xy = sum((x_list[i] - x_mean) * (y_list[i] - y_mean) for i in range(n))
# 斜率
k = L_xy / L_xx
# 截距
b = y_mean - k * x_mean
# 相关系数 r
r = L_xy / Decimal(str(math.sqrt(float(L_xx * L_yy))))
# 斜率的标准不确定度 u_k
# u_k = k * sqrt( (1/r^2 - 1) / (n - 2) )
r_float = float(r)
if abs(r_float) < 1.0:
u_k_float = float(k) * math.sqrt( (1.0 / (r_float**2) - 1.0) / (n - 2) )
else:
u_k_float = 0.0
u_k = Decimal(str(abs(u_k_float)))
return k, b, r, u_k
def input_data_group(name, count, unit=""):
"""输入一组重复测量的数值"""
prompt_unit = f" (单位: {unit})" if unit else ""
print(f"\n请输入 {name} 的 {count} 次测量值{prompt_unit}:")
data = []
for i in range(count):
while True:
try:
val = input(f"第 {i+1} 次 = ").strip()
if not val: continue
data.append(Decimal(val))
break
except Exception:
print("输入无效,请输入数字。")
return data
def main(): def main():
print("========================================") print("========================================")
@@ -196,15 +121,15 @@ def main():
# 输入拉脱电压 U_pull (6次) # 输入拉脱电压 U_pull (6次)
U_pull_vals = input_data_group("拉脱时的电压差/峰值电压 U_pull", 6, "同标定电压单位") U_pull_vals = input_data_group("拉脱时的电压差/峰值电压 U_pull", 6, "同标定电压单位")
U_pull_mean, U_pull_u = calculate_stats(U_pull_vals, delta_U) U_pull_mean, U_pull_u = calculate_stats(U_pull_vals, delta_U)[:2]
# 输入内径 d1 (6次) # 输入内径 d1 (6次)
d1_vals = input_data_group("铁环内径 d1", 6, "mm") d1_vals = input_data_group("铁环内径 d1", 6, "mm")
d1_mean, d1_u = calculate_stats(d1_vals, delta_d) d1_mean, d1_u = calculate_stats(d1_vals, delta_d)[:2]
# 输入外径 d2 (6次) # 输入外径 d2 (6次)
d2_vals = input_data_group("铁环外径 d2", 6, "mm") d2_vals = input_data_group("铁环外径 d2", 6, "mm")
d2_mean, d2_u = calculate_stats(d2_vals, delta_d) d2_mean, d2_u = calculate_stats(d2_vals, delta_d)[:2]
# --- 3. 表面张力系数计算 --- # --- 3. 表面张力系数计算 ---
# F = U / K # F = U / K
+6 -50
View File
@@ -38,55 +38,11 @@ Viscosity Coefficient Calculator (Stokes' Law)
u_B,X = Δ_inst / sqrt(3) u_B,X = Δ_inst / sqrt(3)
""" """
def scientific_round(value, uncertainty): from utils import (
""" scientific_round,
按照“四舍六入五凑偶”修约: calculate_stats,
1. 不确定度 u 保留一位有效数字 input_data_group
2. 平均值末位与 u 对齐 )
"""
if uncertainty <= 0:
return str(value), "0"
u_float = float(uncertainty)
first_digit_pos = math.floor(math.log10(u_float))
prec = Decimal('1e' + str(first_digit_pos))
u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN)
val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN)
return val_final, u_final
def calculate_stats(data_list, delta_inst):
"""计算一组数据的平均值、A类、B类及合成不确定度"""
n = len(data_list)
mean = sum(data_list) / Decimal(n)
if n > 1:
variance = sum((x - mean)**2 for x in data_list) / Decimal(n - 1)
s = Decimal(str(math.sqrt(float(variance))))
u_a = s / Decimal(str(math.sqrt(n)))
else:
u_a = Decimal("0")
u_b = Decimal(delta_inst) / Decimal(str(math.sqrt(3)))
u_combined = Decimal(str(math.sqrt(float(u_a**2 + u_b**2))))
return mean, u_combined
def input_data_group(name, count, unit=""):
"""输入一组重复测量的数值"""
print(f"\n请输入 {name} 的 {count} 次测量值" + (f" (单位: {unit})" if unit else "") + ":")
data = []
for i in range(count):
while True:
try:
val = input(f"第 {i+1} 次 = ").strip()
if not val: continue
data.append(Decimal(val))
break
except Exception:
print("输入无效,请输入数字。")
return data
def main(): def main():
print("========================================") print("========================================")
@@ -129,7 +85,7 @@ def main():
# 输入5个时间数据 # 输入5个时间数据
t_vals = input_data_group("小球下落时间 t", 5, "s") t_vals = input_data_group("小球下落时间 t", 5, "s")
t_mean, t_u = calculate_stats(t_vals, delta_t) t_mean, t_u = calculate_stats(t_vals, delta_t)[:2]
# --- 3. 粘滞系数计算 --- # --- 3. 粘滞系数计算 ---
# 统一转化为国际标准单位 (m, kg, s) # 统一转化为国际标准单位 (m, kg, s)
+9 -60
View File
@@ -20,62 +20,11 @@ Young's Modulus Calculator (Optical Lever & Successive Difference Method)
u_B,X = Δ_inst / sqrt(3) u_B,X = Δ_inst / sqrt(3)
""" """
def scientific_round(value, uncertainty): from utils import (
""" scientific_round,
按照“四舍六入五凑偶”修约: calculate_stats,
1. 不确定度 u 保留一位有效数字(若首位是1或2,有时保留两位,此处统一保留一位) input_data_group
2. 平均值末位与 u 对齐 )
"""
if uncertainty <= 0:
return str(value), "0"
u_float = float(uncertainty)
# 找到第一位非零数字的位置
first_digit_pos = math.floor(math.log10(u_float))
prec = Decimal('1e' + str(first_digit_pos))
# 不确定度修约
u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN)
# 平均值修约,保持与不确定度末位对齐
val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN)
return val_final, u_final
def calculate_stats(data_list, delta_inst):
"""计算一组数据的平均值、A类、B类及合成不确定度"""
n = len(data_list)
mean = sum(data_list) / Decimal(n)
# A类不确定度
if n > 1:
variance = sum((x - mean)**2 for x in data_list) / Decimal(n - 1)
s = Decimal(str(math.sqrt(float(variance))))
u_a = s / Decimal(str(math.sqrt(n)))
else:
u_a = Decimal("0")
# B类不确定度
u_b = Decimal(delta_inst) / Decimal(str(math.sqrt(3)))
# 合成
u_combined = Decimal(str(math.sqrt(float(u_a**2 + u_b**2))))
return mean, u_combined
def input_data_group(name, count, unit="mm"):
"""输入一组重复测量的数值"""
print(f"\n请输入 {name} 的 {count} 次测量值 (单位: {unit}):")
data = []
for i in range(count):
while True:
try:
val = input(f"{name}_{i+1} = ").strip()
if not val: continue
data.append(Decimal(val))
break
except Exception:
print("输入无效,请输入数字。")
return data
def main(): def main():
# --- 1. 常量设置 --- # --- 1. 常量设置 ---
@@ -102,10 +51,10 @@ def main():
delta_d = Decimal(input("d 的仪器误差 (mm, 默认 0.005): ").strip() or "0.005") delta_d = Decimal(input("d 的仪器误差 (mm, 默认 0.005): ").strip() or "0.005")
delta_b = Decimal(input("b 的仪器误差 (mm, 默认 0.02): ").strip() or "0.02") delta_b = Decimal(input("b 的仪器误差 (mm, 默认 0.02): ").strip() or "0.02")
L_mean, L_u = calculate_stats(L_vals, delta_L) L_mean, L_u = calculate_stats(L_vals, delta_L)[:2]
D_mean, D_u = calculate_stats(D_vals, delta_D) D_mean, D_u = calculate_stats(D_vals, delta_D)[:2]
d_mean, d_u = calculate_stats(d_vals, delta_d) d_mean, d_u = calculate_stats(d_vals, delta_d)[:2]
b_mean, b_u = calculate_stats(b_vals, delta_b) b_mean, b_u = calculate_stats(b_vals, delta_b)[:2]
# --- 3. 刻度值输入 (0-7个砝码, 加重+减重) --- # --- 3. 刻度值输入 (0-7个砝码, 加重+减重) ---
print("\n--- 输入刻度尺读数 n (单位: mm) ---") print("\n--- 输入刻度尺读数 n (单位: mm) ---")
+6 -54
View File
@@ -1,55 +1,11 @@
import math import math
from decimal import Decimal, ROUND_HALF_EVEN from decimal import Decimal, ROUND_HALF_EVEN
def parse_angle_to_minutes(s): from utils import (
""" parse_angle_to_minutes,
将输入解析为内部统一单位:分钟(Decimal)。 format_minutes_as_angle,
支持格式: scientific_round
1. 'D.M' -> D度M分 (例如 '120.15' -> 120*60 + 15 = 7215分) )
2. 'D' (纯数字无点) -> D度 (例如 '120' -> 120*60 = 7200分)
3. 'D.M.S' -> D度M分S秒 (例如 '120.15.30' -> 7215.5分)
"""
parts = s.split('.')
if len(parts) == 1:
# 视为度
return Decimal(parts[0]) * 60
elif len(parts) == 2:
# 度.分
return Decimal(parts[0]) * 60 + Decimal(parts[1])
else:
# 度.分.秒 或更多
res = Decimal(parts[0]) * 60 + Decimal(parts[1])
if len(parts) >= 3:
res += Decimal(parts[2]) / 60
return res
def format_minutes_as_angle(minutes, prec=None):
"""
将总分钟数转换为 D° M' 格式,支持保持对齐精度。
"""
neg = minutes < 0
minutes = abs(minutes)
d = int(minutes // 60)
m = minutes % 60
# 进位处理:如果 m 由于位宽限制进位到 60
if m >= 60:
d += 1
m -= 60
if prec is not None and prec < 1:
# 计算小数位数
places = abs(prec.as_tuple().exponent)
m_str = format(m, f'.{places}f')
else:
# 整数或自动小数位
if m == int(m):
m_str = str(int(m))
else:
m_str = format(m, 'f').rstrip('0').rstrip('.')
return f"{'-' if neg else ''}{d}°{m_str}'"
def calc_uncertainty(data_strs, res_str="1"): def calc_uncertainty(data_strs, res_str="1"):
# 转换为分钟进行高精度计算 # 转换为分钟进行高精度计算
@@ -89,15 +45,11 @@ def format_final_result(mean_minutes, u_minutes):
if u_float == 0: if u_float == 0:
return f"θ = {format_minutes_as_angle(mean_minutes)} ± 0'" return f"θ = {format_minutes_as_angle(mean_minutes)} ± 0'"
# 找到第一位非零数字的位置
first_digit_pos = math.floor(math.log10(u_float)) first_digit_pos = math.floor(math.log10(u_float))
prec = Decimal('1e' + str(first_digit_pos)) prec = Decimal('1e' + str(first_digit_pos))
# 修约 mean_final_val, u_final = scientific_round(mean_minutes, u_minutes)
u_final = u_minutes.quantize(prec, rounding=ROUND_HALF_EVEN)
mean_final_val = mean_minutes.quantize(prec, rounding=ROUND_HALF_EVEN)
# 转换为角度字符串输出
mean_str = format_minutes_as_angle(mean_final_val, prec) mean_str = format_minutes_as_angle(mean_final_val, prec)
u_str = f"{u_final}'" u_str = f"{u_final}'"
+2 -19
View File
@@ -1,14 +1,7 @@
import math import math
from decimal import Decimal, ROUND_HALF_EVEN from decimal import Decimal, ROUND_HALF_EVEN
def get_decimal_places(data_strings): from utils import get_decimal_places, scientific_round
"""获取输入数据中小数点后的位数"""
max_places = 0
for s in data_strings:
if '.' in s:
places = len(s.split('.')[1])
max_places = max(max_places, places)
return max_places
def calc_uncertainty(data_strs, res_str="0.05"): def calc_uncertainty(data_strs, res_str="0.05"):
# 转换为 Decimal 进行高精度计算 # 转换为 Decimal 进行高精度计算
@@ -44,17 +37,7 @@ def format_final_result(mean, u):
1. 不确定度 u 保留一位有效数字 1. 不确定度 u 保留一位有效数字
2. 平均值末位与 u 对齐 2. 平均值末位与 u 对齐
""" """
u_float = float(u) mean_final, u_final = scientific_round(mean, u)
# 找到第一位非零数字的位置
first_digit_pos = math.floor(math.log10(u_float))
prec = Decimal('1e' + str(first_digit_pos))
# 修改:不确定度 u 的修约改为 ROUND_HALF_EVEN
u_final = u.quantize(prec, rounding=ROUND_HALF_EVEN)
# 修改:平均值修约保持一致
mean_final = mean.quantize(prec, rounding=ROUND_HALF_EVEN)
return f"f = {mean_final} ± {u_final}" return f"f = {mean_final} ± {u_final}"
if __name__ == "__main__": if __name__ == "__main__":
+16
View File
@@ -0,0 +1,16 @@
from utils.formatting import (
get_decimal_places,
scientific_round,
parse_angle_to_minutes,
format_minutes_as_angle
)
from utils.stats import (
calculate_stats,
linear_regression
)
from utils.io import (
input_float,
input_decimal,
input_int,
input_data_group
)
+76
View File
@@ -0,0 +1,76 @@
import math
from decimal import Decimal, ROUND_HALF_EVEN
def get_decimal_places(data_strings):
"""获取输入数据中小数点后的位数,用于自适应展示"""
max_places = 0
for s in data_strings:
if '.' in s:
places = len(s.split('.')[1])
max_places = max(max_places, places)
return max_places
def scientific_round(value, uncertainty):
"""
按照“四舍六入五凑偶”修约:
1. 不确定度 u 保留一位有效数字
2. 平均值/结果末位与 u 对齐
"""
if uncertainty <= 0:
return str(value), "0"
d_val = Decimal(str(value))
d_unc = Decimal(str(uncertainty))
u_float = float(d_unc)
first_digit_pos = math.floor(math.log10(u_float))
prec = Decimal('1e' + str(first_digit_pos))
u_final = d_unc.quantize(prec, rounding=ROUND_HALF_EVEN)
val_final = d_val.quantize(prec, rounding=ROUND_HALF_EVEN)
return val_final, u_final
def parse_angle_to_minutes(s):
"""
将输入解析为内部统一单位:分钟(Decimal)。
支持格式:
1. 'D.M' -> D度M分 (例如 '120.15' -> 120*60 + 15 = 7215分)
2. 'D' (纯数字无点) -> D度 (例如 '120' -> 120*60 = 7200分)
3. 'D.M.S' -> D度M分S秒 (例如 '120.15.30' -> 7215.5分)
"""
parts = s.split('.')
if len(parts) == 1:
return Decimal(parts[0]) * 60
elif len(parts) == 2:
return Decimal(parts[0]) * 60 + Decimal(parts[1])
else:
res = Decimal(parts[0]) * 60 + Decimal(parts[1])
if len(parts) >= 3:
res += Decimal(parts[2]) / 60
return res
def format_minutes_as_angle(minutes, prec=None):
"""
将总分钟数转换为 D° M' 格式,支持保持对齐精度。
"""
neg = minutes < 0
minutes = abs(minutes)
d = int(minutes // 60)
m = minutes % 60
if m >= 60:
d += 1
m -= 60
if prec is not None and prec < 1:
places = abs(prec.as_tuple().exponent)
m_str = format(m, f'.{places}f')
else:
if m == int(m):
m_str = str(int(m))
else:
m_str = format(m, 'f').rstrip('0').rstrip('.')
return f"{'-' if neg else ''}{d}°{m_str}'"
+59
View File
@@ -0,0 +1,59 @@
from decimal import Decimal
def input_float(prompt, default=None):
"""输入一个浮点数值,支持默认值"""
while True:
try:
suffix = f" (默认 {default})" if default is not None else ""
val = input(f"{prompt}{suffix}: ").strip()
if not val and default is not None:
return float(default)
if not val:
continue
return float(val)
except Exception:
print(" 输入无效,请输入数字。")
def input_decimal(prompt, default=None):
"""输入一个 Decimal 值,支持默认值"""
while True:
try:
suffix = f" (默认 {default})" if default is not None else ""
val = input(f"{prompt}{suffix}: ").strip()
if not val and default is not None:
return Decimal(str(default))
if not val:
continue
return Decimal(val)
except Exception:
print(" 输入无效,请输入数字。")
def input_int(prompt, default=None):
"""输入一个整数,支持默认值"""
while True:
try:
suffix = f" (默认 {default})" if default is not None else ""
val = input(f"{prompt}{suffix}: ").strip()
if not val and default is not None:
return default
if not val:
continue
return int(val)
except Exception:
print(" 输入无效,请输入整数。")
def input_data_group(name, count, unit=""):
"""输入一组重复测量的数值"""
prompt_unit = f" (单位: {unit})" if unit else ""
print(f"\n请输入 {name} 的 {count} 次测量值{prompt_unit}:")
data = []
for i in range(count):
while True:
try:
val = input(f"第 {i+1} 次 = ").strip()
if not val: continue
data.append(Decimal(val))
break
except Exception:
print("输入无效,请输入数字。")
return data
+59
View File
@@ -0,0 +1,59 @@
import math
from decimal import Decimal
def calculate_stats(data_list, delta_inst):
"""
计算一组数据的平均值、A类、B类及合成不确定度。
支持输入 list of Decimal 或 list of float/str/int。
返回: (mean, u_combined, u_a, u_b, s)
"""
n = len(data_list)
decimal_data = [Decimal(str(x)) for x in data_list]
mean = sum(decimal_data) / Decimal(n)
if n > 1:
variance = sum((x - mean)**2 for x in decimal_data) / Decimal(n - 1)
s = Decimal(str(math.sqrt(float(variance))))
u_a = s / Decimal(str(math.sqrt(n)))
else:
s = Decimal("0")
u_a = Decimal("0")
u_b = Decimal(str(delta_inst)) / Decimal(str(math.sqrt(3)))
u_combined = Decimal(str(math.sqrt(float(u_a**2 + u_b**2))))
return mean, u_combined, u_a, u_b, s
def linear_regression(x_list, y_list):
"""
最小二乘法线性回归: y = k·x + b
返回: (k, b, r, u_k)
u_k = |k| · √[(1/r² − 1) / (n − 2)]
支持 input numbers as Decimal or float.
"""
n = len(x_list)
x_dec = [Decimal(str(x)) for x in x_list]
y_dec = [Decimal(str(y)) for y in y_list]
x_mean = sum(x_dec) / Decimal(n)
y_mean = sum(y_dec) / Decimal(n)
L_xx = sum((x - x_mean)**2 for x in x_dec)
L_yy = sum((y - y_mean)**2 for y in y_dec)
L_xy = sum((x_dec[i] - x_mean) * (y_dec[i] - y_mean) for i in range(n))
k = L_xy / L_xx if L_xx != 0 else Decimal("0")
b = y_mean - k * x_mean
denom = Decimal(str(math.sqrt(float(L_xx * L_yy)))) if L_xx * L_yy > 0 else Decimal("1")
r = L_xy / denom
r_float = float(r)
if abs(r_float) < 1.0 and n > 2:
u_k_float = float(abs(k)) * math.sqrt((1.0 / (r_float**2) - 1.0) / (n - 2))
else:
u_k_float = 0.0
u_k = Decimal(str(u_k_float))
return k, b, r, u_k