def test_fp_frac_width_overflow(): with pytest.raises(InvalidNumericType, match=r"overflow"): FixedPoint("0.25", 2, 1, False) with pytest.raises(InvalidNumericType, match=r"overflow"): FixedPoint("0b111", 2, 1, True) with pytest.raises(InvalidNumericType, match=r"overflow"): FixedPoint("0x7", 2, 1, False)
def fp_round_trip(bit_string: str) -> FixedPoint: # Round-trips the fixed point conversion. bin = FixedPoint(f"0b{bit_string}", width, int_width, is_signed).str_value() hex = FixedPoint(f"0x{hex_string}", width, int_width, is_signed).str_value() assert bin == hex return FixedPoint( bin, width, int_width, is_signed, )
def convert(x): if not is_fp: return Bitnum(x, **shape[k]).base_64_encode() try: return FixedPoint(x, **shape[k]).base_64_encode() except InvalidNumericType as error: if round_float_to_fixed: # Only round if it is not already representable. fractional_width = width - int_width x = float_to_fixed(float(x), fractional_width) x = str(x) return FixedPoint(x, **shape[k]).base_64_encode() else: raise error
def parse_entry(target, format_details): numeric_type, is_signed, (width, int_width, frac_width) = ( format_details if format_details is not None else ("bitnum", False, (None, None, None)) ) if isinstance(target, list): return [ parse_entry( x, (numeric_type, is_signed, (width, int_width, frac_width)) ) for x in target ] elif isinstance(target, str): num = base64.standard_b64decode(target) int_rep = int.from_bytes(num, "little", signed=is_signed) if int_rep > 0 and (int_rep & (1 << (width - 1))) and is_signed: int_rep = -(2 ** (width - 1)) + (int_rep ^ (1 << (width - 1))) if numeric_type == "bitnum": return int_rep elif numeric_type == "fixed_point": bin_str = bin(int.from_bytes(num, "little", signed=False)) bin_len = len(bin_str[2:]) if bin_len < width: bin_str = "0b" + ("0" * (width - bin_len)) + bin_str[2:] assert len(bin_str) == width + 2 fp = FixedPoint( bin_str, width, int_width, is_signed, ) return fp.str_value() else: return False, f"got {numeric_type}"
def exp(x: str, width: int, int_width: int, is_signed: bool, print_results=False): """ Computes an approximation of e^x. This is done by splitting the fixed point number x into its integral bits `i` and fractional bits `f`, and computing e^(i + f) = e^i * e^f. For the fractional portion, a chebyshev approximation is used. Example: exp( x="1.0", width=32, int_width=16, is_signed=True, print_results=True ) # Should return an approximation of e^(1.0) """ frac_width = width - int_width bin_string = FixedPoint(x, width, int_width, is_signed=False).bit_string(with_prefix=False) int_b = bin_string[:int_width] int_bin = int(int_b, 2) frac_b = bin_string[int_width:width] frac_bin = int(frac_b, 2) # Split e^x into e^i * e^f. e_i = Decimal("2.71828")**int_bin e_table = compute_exp_frac_table(frac_width) e_f = e_table[frac_bin] # Compute e^i * e^f. actual = Decimal(e_i) * Decimal(e_f) if print_results: accepted = Decimal("2.71828")**Decimal(x) print(f"e^{x}: {accepted}, actual: {actual}" f"relative difference: {(actual - accepted) / actual * 100} %") return actual
def test_fp_int_width_overflow(): with pytest.raises(InvalidNumericType, match=r"overflow"): FixedPoint("2.0", 2, 1, False) with pytest.raises(InvalidNumericType, match=r"overflow"): FixedPoint("0b101", 2, 1, True)
def test_non_string_initialization(): with pytest.raises(InvalidNumericType, match=r"string"): Bitnum(16, 5, False) with pytest.raises(InvalidNumericType, match=r"string"): FixedPoint(0.5, 2, 1, False)
def test_empty_string(): with pytest.raises(InvalidNumericType, match=r"non-empty string"): Bitnum("", 2, False) with pytest.raises(InvalidNumericType, match=r"non-empty string"): FixedPoint("", 2, 1, False)
def test_unsigned_negative_number(): with pytest.raises(InvalidNumericType, match=r"negative value"): FixedPoint("-0.5", 2, 1, False) with pytest.raises(InvalidNumericType, match=r"negative value"): Bitnum("-1", 2, False)
def test_fp_invalid_representation(): with pytest.raises(InvalidNumericType, match=r"not representable"): FixedPoint("0.1", 2, 1, False)