chronaxis.core.numeric_point
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""生产逐点计算的定宽数值原语,不构造单元素数组。23输入是编译器已核实类型和单位的ScalarValue。输出保留数值类型、无效原因和4有限精度要求;整数用无界临时值验证定宽结果,float转换先检查有效尾位。5这些函数不调用参考数值实现,不读写来源,也不持有跨点状态。6"""78from math import isfinite, sqrt9import operator1011from .scalar import INTEGER_BOUNDS, ScalarValue12from .types import QualityFlag as Q, SignalDType as D, _QUALITY_BY_MASK1314_COMPARE = {'eq': operator.eq, 'ne': operator.ne, 'lt': operator.lt,15 'le': operator.le, 'gt': operator.gt, 'ge': operator.ge}16_ARITHMETIC = {'add': operator.add, 'sub': operator.sub,17 'mul': operator.mul, 'div': operator.truediv}181920def failed(dtype, quality, labels=()):21 """构造不可用结果。参数: 目标类型、真实原因、枚举域。返回: ScalarValue。异常: 模型合同错误。"""22 return ScalarValue(None, dtype, False, quality, labels)232425def exact_float(value):26 """按整数幅度的有效尾位判断float64可表示性。2728 参数: 原生int或float。返回: (有限float或None, 质量)。29 异常: 非数值结构错误;超大整数作为NONFINITE返回,不抛转换溢出。30 """31 if type(value) is float:32 return (value, Q.NONE) if isfinite(value) else (None, Q.NONFINITE)33 if type(value) is not int:34 raise TypeError('逐点浮点转换需要原生数值')35 magnitude = abs(value)36 shift = max(0, magnitude.bit_length() - 53)37 # 超出有限float64的幅度先归类,不能把无穷转换误报为一般精度失败。38 if magnitude.bit_length() > 1024:39 return None, Q.NONFINITE40 try:41 number = float(value)42 except OverflowError:43 return None, Q.NONFINITE44 if not isfinite(number):45 return None, Q.NONFINITE46 if shift and magnitude & ((1 << shift) - 1):47 return None, Q.PRECISION_LOSS48 return number, Q.NONE495051def to_float_point(value):52 """无损转换当前数值。参数: ScalarValue。返回: FLOAT64值。异常: 非数值类型。"""53 if value.dtype not in (*INTEGER_BOUNDS, D.FLOAT64):54 raise TypeError('逐点浮点转换需要数值类型')55 if not value.valid:56 return failed(D.FLOAT64, value.quality)57 number, quality = exact_float(value.value)58 return failed(D.FLOAT64, quality) if quality else ScalarValue(number, D.FLOAT64)596061def checked_result(number, dtype):62 """校验计算完成后的定宽范围或有限性。参数: 数值、静态类型。返回: typed值。异常: 非数值类型。"""63 if dtype in INTEGER_BOUNDS:64 lower, upper = INTEGER_BOUNDS[dtype]65 return (ScalarValue(number, dtype) if lower <= number <= upper66 else failed(dtype, Q.OVERFLOW))67 if dtype is D.FLOAT64:68 return ScalarValue(number, dtype) if isfinite(number) else failed(dtype, Q.NONFINITE)69 raise TypeError('逐点数值结果类型不合法')707172def binary_point(op, left, right, dtype):73 """计算已通过静态类型检查的二元运算。7475 参数: 固定操作名、两侧typed值、编译后的结果dtype。返回: ScalarValue。76 异常: 未登记操作或内部类型合同错误;除零和定宽失败转成质量。77 """78 quality = _QUALITY_BY_MASK[int(left.quality) | int(right.quality)]79 if quality:80 return failed(dtype, quality)81 if left.dtype is D.ENUM and left.labels != right.labels:82 raise ValueError('枚举比较需要相同标签域')83 a, b = left.value, right.value84 floating = dtype is D.FLOAT64 or op in _COMPARE and D.FLOAT64 in (left.dtype, right.dtype)85 if floating:86 a, aq = exact_float(a)87 b, bq = exact_float(b)88 quality = _QUALITY_BY_MASK[int(aq) | int(bq)]89 if quality:90 return failed(dtype, quality)91 # int64/uint64比较直接用精确整数,不经过NumPy混合提升或float。92 if op in _COMPARE:93 return ScalarValue(_COMPARE[op](a, b), D.BOOL)94 if op == 'div' and b == 0:95 return failed(dtype, Q.DOMAIN_ERROR)96 return checked_result(_ARITHMETIC[op](a, b), dtype)979899def unary_point(op, value):100 """计算取负或绝对值。参数: 操作名、typed数值。返回: 同型值。异常: 未知操作。"""101 if not value.valid:102 return failed(value.dtype, value.quality)103 number = abs(value.value) if op == 'abs' else -value.value if op == 'neg' else None104 if number is None:105 raise ValueError('未登记的逐点单目运算')106 return checked_result(number, value.dtype)107108109def affine_point(value, scale, offset, dtype):110 """按完整value*scale+offset换算当前值,不进行第二次DBC换算。111112 参数: 已编译的数值、系数和目标dtype。返回: ScalarValue。113 异常: 内部静态合同错误;数值失败保存为质量。114 """115 if not value.valid:116 return failed(dtype, value.quality)117 if dtype in INTEGER_BOUNDS:118 if value.dtype not in INTEGER_BOUNDS or type(scale) is not int or type(offset) is not int:119 raise ValueError('整数仿射转换需要整数输入及系数')120 return checked_result(value.value * scale + offset, dtype)121 number, nq = exact_float(value.value)122 factor, fq = exact_float(scale)123 shift, sq = exact_float(offset)124 quality = _QUALITY_BY_MASK[int(nq) | int(fq) | int(sq)]125 if quality:126 return failed(dtype, quality)127 result = number * factor + shift128 if not isfinite(result):129 return failed(dtype, Q.NONFINITE)130 # 偏移抵消为零合法,纯倍率把非零压成零才是精度丢失。131 if shift == 0 and factor != 0 and number != 0 and result == 0:132 return failed(dtype, Q.PRECISION_LOSS)133 return ScalarValue(result, dtype)134135136def logic_point(op, values):137 """按对称三值规则求布尔值。参数: 固定逻辑操作和typed值。返回: BOOL。异常: 未知操作。"""138 if op == 'not':139 value = values[0]140 return ScalarValue(not value.value, D.BOOL) if value.valid else failed(D.BOOL, value.quality)141 if op not in ('and', 'or'):142 raise ValueError('未登记的逐点逻辑操作')143 deciding = op == 'or'144 quality = Q.NONE145 for value in values:146 quality = _QUALITY_BY_MASK[int(quality) | int(value.quality)]147 if value.valid and value.value is deciding:148 return ScalarValue(deciding, D.BOOL)149 return failed(D.BOOL, quality) if quality else ScalarValue(not deciding, D.BOOL)150151152def math_point(op, values):153 """执行极值、限幅和开方。参数: 已统一类型/单位的实参。返回: typed结果。异常: 未知操作。"""154 first = values[0]155 if op == 'sqrt':156 if not first.valid:157 return failed(D.FLOAT64, first.quality)158 if first.value < 0:159 return failed(D.FLOAT64, Q.DOMAIN_ERROR)160 number = to_float_point(first)161 return number if not number.valid else ScalarValue(sqrt(number.value), D.FLOAT64)162 quality = Q.NONE163 for value in values:164 quality = _QUALITY_BY_MASK[int(quality) | int(value.quality)]165 if op == 'clip':166 lower, upper = values[1:]167 # 即使输入无效,只要两边界有效且倒置,也应保留这个域错误。168 if lower.valid and upper.valid and lower.value > upper.value:169 quality = _QUALITY_BY_MASK[int(quality) | int(Q.DOMAIN_ERROR)]170 if quality:171 return failed(first.dtype, quality)172 result = lower if first.value < lower.value else upper if first.value > upper.value else first173 return ScalarValue(result.value, first.dtype)174 if op not in ('minimum', 'maximum'):175 raise ValueError('未登记的逐点数值函数')176 if quality:177 return failed(first.dtype, quality)178 number = first.value179 compare = operator.lt if op == 'minimum' else operator.gt180 # 严格比较保留第一个相等参数及其负零符号。181 for value in values[1:]:182 if compare(value.value, number):183 number = value.value184 return ScalarValue(number, first.dtype)185186187def integer_point(node, values):188 """执行定宽取余、位操作和精确转整数。189190 参数: 编译节点及typed实参。返回: 静态dtype结果。191 异常: 未知操作;小数、越界、零除保存对应质量。192 """193 value = values[0]194 quality = value.quality195 for item in values[1:]:196 quality = _QUALITY_BY_MASK[int(quality) | int(item.quality)]197 if quality:198 return failed(node.dtype, quality)199 number = value.value200 if node.operator == 'mod':201 divisor = values[1].value202 return failed(node.dtype, Q.DOMAIN_ERROR) if divisor == 0 else ScalarValue(number % divisor, node.dtype)203 if node.operator in ('bit_test', 'bit_extract'):204 start, width = node.value205 bits = ((number & ((1 << 64) - 1)) >> start) & ((1 << width) - 1)206 return ScalarValue(bool(bits) if node.dtype is D.BOOL else bits, node.dtype)207 if node.operator not in ('to_int64', 'to_uint64'):208 raise ValueError('未登记的逐点整数操作')209 if value.dtype is D.FLOAT64 and not number.is_integer():210 return failed(node.dtype, Q.PRECISION_LOSS)211 number = int(number)212 return checked_result(number, node.dtype)213214215def lookup_point(table, axis, nodes, value):216 """用准备期只读表视图查一个有效点,不建立单元素列。217218 参数: 已核表、只读轴和值列、typed输入。返回: 表声明类型的值。219 异常: 静态表结构错误;范围、精度及非有限结果保存为质量。220 """221 query = to_float_point(value)222 if not query.valid:223 return failed(table.dtype, query.quality)224 x = query.value225 # searchsorted只返回标量下标;轴和值视图由计划持有,不在每点重建。226 hi = int(axis.searchsorted(x, side='left'))227 count = len(axis)228 index = min(hi, count - 1)229 if x == float(axis[index]):230 return ScalarValue(nodes[index].item(), table.dtype)231 if hi == 0 or hi == count:232 return (ScalarValue(nodes[index].item(), table.dtype) if table.bounds == 'clamp'233 else failed(table.dtype, Q.OUT_OF_RANGE))234 if table.mode != 'linear':235 return ScalarValue(nodes[hi - 1 if table.mode == 'previous' else hi].item(), table.dtype)236 left, right = float(axis[hi - 1]), float(axis[hi])237 y0, y1 = float(nodes[hi - 1]), float(nodes[hi])238 if y0 == y1:239 return ScalarValue(y0, table.dtype)240 span = right - left241 fraction = (x - left) / span242 # 同号避免不必要的乘加抵消;异号不形成可能溢出的y1-y0。243 result = (y0 + fraction * (y1 - y0) if (y0 >= 0) == (y1 >= 0)244 else (1.0 - fraction) * y0 + fraction * y1)245 if not isfinite(span) or not isfinite(result):246 return failed(table.dtype, Q.NONFINITE)247 if fraction == 0 or fraction == 1:248 return failed(table.dtype, Q.PRECISION_LOSS)249 return ScalarValue(result, table.dtype)250