From e0b1d22f6b44dcb320185fce8aef105363b95c92 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Sat, 21 Feb 2026 17:40:46 -0800 Subject: [PATCH 01/21] Generated Tests 1 and 2 for B5 Converts --- Makefile | 3 ++ src/cover_float/cli.py | 3 ++ src/cover_float/common/constants.py | 25 ++++++++++ src/cover_float/testgen/B5.py | 73 +++++++++++++++++++++++++++++ src/cover_float/testgen/__init__.py | 1 + 5 files changed, 105 insertions(+) create mode 100644 src/cover_float/testgen/B5.py diff --git a/Makefile b/Makefile index 7fd0cf8..34b747a 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,9 @@ sim: B1: uv run --managed-python cover-float-testgen --model B1 +B5: + uv run --managed-python cover-float-testgen --model B5 + B9: uv run --managed-python cover-float-testgen --model B9 diff --git a/src/cover_float/cli.py b/src/cover_float/cli.py index 3729cba..0db34e0 100644 --- a/src/cover_float/cli.py +++ b/src/cover_float/cli.py @@ -37,12 +37,15 @@ def testgen() -> None: if args.models is None: tg.B1.main() + tg.B5.main() tg.B9.main() tg.B10.main() tg.B12.main() else: if "B1" in args.models: tg.B1.main() + if "B5" in args.models: + tg.B5.main() if "B9" in args.models: tg.B9.main() if "B10" in args.models: diff --git a/src/cover_float/common/constants.py b/src/cover_float/common/constants.py index fc0625a..75ca62e 100644 --- a/src/cover_float/common/constants.py +++ b/src/cover_float/common/constants.py @@ -58,6 +58,31 @@ # Defining floating point format constants +UNBIASED_EXP = { + FMT_HALF : [-14, 15], + FMT_SINGLE : [-126, 127], + FMT_DOUBLE : [-1022, 1023], + FMT_QUAD : [-16382, 16383], + FMT_BF16 : [-126, 127] +} + +EXPONENT_BIAS = { + FMT_HALF : 15, + FMT_SINGLE : 127, + FMT_DOUBLE : 1023, + FMT_QUAD : 16383, + FMT_BF16 : 127 +} + +ROUNDING_MODES = { + ROUND_NEAR_EVEN, + ROUND_MINMAG, + ROUND_MIN, + ROUND_MAX, + ROUND_NEAR_MAXMAG, + ROUND_ODD +} + MANTISSA_BITS = { FMT_HALF : 10, FMT_SINGLE: 23, diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py new file mode 100644 index 0000000..f2b235e --- /dev/null +++ b/src/cover_float/testgen/B5.py @@ -0,0 +1,73 @@ +#Lamarr +#B5 Model + + +import random +from cover_float.reference import run_and_store_test_vector +from cover_float.common.constants import UNBIASED_EXP, OP_CFF, FMT_QUAD, FMT_DOUBLE, FMT_SINGLE, FMT_BF16, FMT_HALF, ROUNDING_MODES, MANTISSA_BITS, ROUNDING_MODES, EXPONENT_BITS, EXPONENT_BIAS + + +B5_FMTS = [FMT_QUAD, FMT_DOUBLE, FMT_SINGLE, FMT_BF16, FMT_HALF] + + +def generate_FP(input_e_bitwidth, input_sign, input_exponent, input_mantissa, input_bias): + exponent = f"{input_exponent + input_bias:0{input_e_bitwidth}b}" + complete = input_sign + exponent + input_mantissa + fp_complete = f"{int(complete, 2):032X}" + + return fp_complete + + +def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): + hp_m_bits = MANTISSA_BITS[hp] + hp_e_bits = EXPONENT_BITS[hp] + hp_e_bias = EXPONENT_BIAS[hp] + lp_min_exp = UNBIASED_EXP[lp][0] - 1 #Account for subnorms + lp_m_bits = MANTISSA_BITS[lp] + + hp_min_exp = lp_min_exp + + if(hp != FMT_SINGLE and lp != FMT_BF16): + hp_min_exp = lp_min_exp - (lp_m_bits - 1) + + input_1_exponent = random.randint(hp_min_exp, lp_min_exp) + input_2_exponent = random.randint(hp_min_exp, lp_min_exp) + + input_1_trailing_0s = (hp_m_bits - lp_m_bits) + (lp_min_exp - input_1_exponent) + input_2_trailing_0s = (hp_m_bits - lp_m_bits) + (lp_min_exp - input_2_exponent) + + input_1_min_mantissa = int('1'+ input_1_trailing_0s * '0', 2) + input_2_min_mantissa = int('1'+ input_2_trailing_0s * '0', 2) + max_mantissa = int('1'* hp_m_bits, 2) + + input_1_mantissa = format(random.randint(input_1_min_mantissa, max_mantissa), '0b') + input_2_mantissa = format(random.randint(input_2_min_mantissa, max_mantissa), '0b') + + input_value_1 = generate_FP(hp_e_bits, '0', input_1_exponent, input_1_mantissa, hp_e_bias) + input_value_2 = generate_FP(hp_e_bits, '1', input_2_exponent, input_2_mantissa, hp_e_bias) + + run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 + run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 2 + +# def test_conversion_3(lp_min_exp, hp_min_exp, lp_opcode, hp_opcode, rounding_mode, hp_m_bits, hp_e_bits, ): + +def convertTests(test_f, cover_f): + #All conversion tests: + for i_hp in range(len(B5_FMTS)): + hp = B5_FMTS[i_hp] + for i_lp in range(i_hp+1, len(B5_FMTS)): + lp = B5_FMTS[i_lp] + for rounding_mode in ROUNDING_MODES: + tests_conversion_1_2(lp, hp, rounding_mode,test_f, cover_f)#Call tests 1 and 2 + + + + + +def main(): + with open("./tests/testvectors/B5_tv.txt", "w") as test_f, open("./tests/covervectors/B5_cv.txt", "w") as cover_f: + convertTests(test_f, cover_f) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/cover_float/testgen/__init__.py b/src/cover_float/testgen/__init__.py index 225ce4e..1494dc4 100644 --- a/src/cover_float/testgen/__init__.py +++ b/src/cover_float/testgen/__init__.py @@ -1,4 +1,5 @@ import cover_float.testgen.B1 +import cover_float.testgen.B5 import cover_float.testgen.B9 import cover_float.testgen.B10 import cover_float.testgen.B12 \ No newline at end of file From e3b734531e70cd4b3ac3afc203472ef00bca448c Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Sat, 28 Feb 2026 13:43:46 -0800 Subject: [PATCH 02/21] tests 3 and 4 --- src/cover_float/testgen/B5.py | 79 ++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index f2b235e..d8d2b12 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -22,26 +22,23 @@ def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] - lp_min_exp = UNBIASED_EXP[lp][0] - 1 #Account for subnorms + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 #Account for subnorms lp_m_bits = MANTISSA_BITS[lp] + + hp_max_exp = lp_sn_exp - 1 #double check this + hp_min_exp = lp_sn_exp - lp_m_bits - hp_min_exp = lp_min_exp - - if(hp != FMT_SINGLE and lp != FMT_BF16): - hp_min_exp = lp_min_exp - (lp_m_bits - 1) - - input_1_exponent = random.randint(hp_min_exp, lp_min_exp) - input_2_exponent = random.randint(hp_min_exp, lp_min_exp) - - input_1_trailing_0s = (hp_m_bits - lp_m_bits) + (lp_min_exp - input_1_exponent) - input_2_trailing_0s = (hp_m_bits - lp_m_bits) + (lp_min_exp - input_2_exponent) + if(hp == FMT_SINGLE and lp == FMT_BF16): #Different case for FP_32 and BF_16 + hp_min_exp = lp_sn_exp + hp_max_exp = lp_sn_exp + + input_1_exponent = random.randint(hp_min_exp, hp_max_exp) + input_2_exponent = random.randint(hp_min_exp, hp_max_exp) - input_1_min_mantissa = int('1'+ input_1_trailing_0s * '0', 2) - input_2_min_mantissa = int('1'+ input_2_trailing_0s * '0', 2) max_mantissa = int('1'* hp_m_bits, 2) - input_1_mantissa = format(random.randint(input_1_min_mantissa, max_mantissa), '0b') - input_2_mantissa = format(random.randint(input_2_min_mantissa, max_mantissa), '0b') + input_1_mantissa = format(random.randint(0, max_mantissa), '0b') + input_2_mantissa = format(random.randint(0, max_mantissa), '0b') input_value_1 = generate_FP(hp_e_bits, '0', input_1_exponent, input_1_mantissa, hp_e_bias) input_value_2 = generate_FP(hp_e_bits, '1', input_2_exponent, input_2_mantissa, hp_e_bias) @@ -49,8 +46,58 @@ def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 2 +# def genTestVectors3_4(): + +def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): + hp_m_bits = MANTISSA_BITS[hp] + hp_e_bits = EXPONENT_BITS[hp] + hp_e_bias = EXPONENT_BIAS[hp] + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 #Account for subnorms + lp_m_bits = MANTISSA_BITS[lp] + + hp_sn_lp_exp = lp_sn_exp - lp_m_bits + + #MinSN + 1 ulp, MinSN + 2 ulp, MinSN + 3 ulp + for i in range(0, int('11', 2)): # Iterate over differt round and sticky bits + hp_exp = hp_sn_lp_exp + rs = format(i, '0b')#round and sticky bit + remaining_mantissa_bits = hp_m_bits - 2 #find out what part of the mantissa bits left need to be randomized + + complete_binary_1 = rs + format(random.randint(0, int('1'*remaining_mantissa_bits, 2)), '0b')#randomize the rest of the number + complete_binary_2 = rs + format(random.randint(0, int('1'*remaining_mantissa_bits, 2)), '0b') + + generate_FP(hp_e_bits, '0', hp_exp, complete_binary_1, hp_e_bias) + generate_FP(hp_e_bits, '1', hp_exp, complete_binary_2, hp_e_bias) + + #MinSN - 1 ulp, MinSN - 2 ulp + for i in range(int('10',2), int('11',2)): + hp_exp = hp_sn_lp_exp - 1 + s = format(i, '0b')#sticky bit + remaining_mantissa_bits = hp_m_bits - 1 + + complete_binary_1 = s + format(random.randint(0, int('1'*remaining_mantissa_bits, 2)), '0b')#randomize the rest of the number + complete_binary_2 = s + format(random.randint(0, int('1'*remaining_mantissa_bits, 2)), '0b') + + input_1 = generate_FP(hp_e_bits, '0', hp_exp, complete_binary_1, hp_e_bias) + input_2 = generate_FP(hp_e_bits, '1', hp_exp, complete_binary_2, hp_e_bias) + + run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 + run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_2}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 2 + + #MinSN - 3 ulp + hp_exp = hp_sn_lp_exp - 2 + s = format(i, '0b')#sticky bit + remaining_mantissa_bits = hp_m_bits + + complete_binary_1 = format(random.randint(0, int('1'*hp_m_bits, 2)), '0b')#randomize the rest of the number + complete_binary_2 = format(random.randint(0, int('1'*hp_m_bits, 2)), '0b') + + generate_FP(hp_e_bits, '0', hp_exp, complete_binary_1, hp_e_bias) + generate_FP(hp_e_bits, '1', hp_exp, complete_binary_2, hp_e_bias) + + # def test_conversion_3(lp_min_exp, hp_min_exp, lp_opcode, hp_opcode, rounding_mode, hp_m_bits, hp_e_bits, ): - + def convertTests(test_f, cover_f): #All conversion tests: for i_hp in range(len(B5_FMTS)): From 49bee29b79daaa49fe4aad1233489ae7b1d04a06 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Wed, 4 Mar 2026 18:57:02 -0800 Subject: [PATCH 03/21] conversion tests for B5 --- src/cover_float/cli.py | 3 +- src/cover_float/testgen/B5.py | 237 +++++++++++++++++++++++++++------- 2 files changed, 192 insertions(+), 48 deletions(-) diff --git a/src/cover_float/cli.py b/src/cover_float/cli.py index 3099a83..6317082 100644 --- a/src/cover_float/cli.py +++ b/src/cover_float/cli.py @@ -57,8 +57,9 @@ def testgen() -> None: if args.models is None: tg.B1.main() - tg.B5.main() auto_parse("B1", args.output_dir) + tg.B5.main() + auto_parse("B5", args.output_dir) tg.B9.main() auto_parse("B9", args.output_dir) tg.B10.main() diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index d8d2b12..6c902aa 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -13,7 +13,7 @@ def generate_FP(input_e_bitwidth, input_sign, input_exponent, input_mantissa, input_bias): exponent = f"{input_exponent + input_bias:0{input_e_bitwidth}b}" complete = input_sign + exponent + input_mantissa - fp_complete = f"{int(complete, 2):032X}" + fp_complete = format(int(complete, 2), "X") return fp_complete @@ -22,12 +22,12 @@ def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 #Account for subnorms + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 #Make the exponent for subnorm values lp_m_bits = MANTISSA_BITS[lp] - hp_max_exp = lp_sn_exp - 1 #double check this + hp_max_exp = lp_sn_exp - 1 hp_min_exp = lp_sn_exp - lp_m_bits - + if(hp == FMT_SINGLE and lp == FMT_BF16): #Different case for FP_32 and BF_16 hp_min_exp = lp_sn_exp hp_max_exp = lp_sn_exp @@ -37,16 +37,21 @@ def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): max_mantissa = int('1'* hp_m_bits, 2) - input_1_mantissa = format(random.randint(0, max_mantissa), '0b') - input_2_mantissa = format(random.randint(0, max_mantissa), '0b') - + input_1_mantissa = f"{random.randint(0, max_mantissa):0{hp_m_bits}b}" + input_2_mantissa = f"{random.randint(0, max_mantissa):0{hp_m_bits}b}" + input_value_1 = generate_FP(hp_e_bits, '0', input_1_exponent, input_1_mantissa, hp_e_bias) input_value_2 = generate_FP(hp_e_bits, '1', input_2_exponent, input_2_mantissa, hp_e_bias) run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 2 -# def genTestVectors3_4(): +def genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f): + input_value_1 = generate_FP(hp_e_bits, '0', hp_exp, complete_binary_1, hp_e_bias) + input_value_2 = generate_FP(hp_e_bits, '1', hp_exp, complete_binary_2, hp_e_bias) + + run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 + run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 2 def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] @@ -57,56 +62,194 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): hp_sn_lp_exp = lp_sn_exp - lp_m_bits - #MinSN + 1 ulp, MinSN + 2 ulp, MinSN + 3 ulp - for i in range(0, int('11', 2)): # Iterate over differt round and sticky bits - hp_exp = hp_sn_lp_exp - rs = format(i, '0b')#round and sticky bit - remaining_mantissa_bits = hp_m_bits - 2 #find out what part of the mantissa bits left need to be randomized - - complete_binary_1 = rs + format(random.randint(0, int('1'*remaining_mantissa_bits, 2)), '0b')#randomize the rest of the number - complete_binary_2 = rs + format(random.randint(0, int('1'*remaining_mantissa_bits, 2)), '0b') + if(hp == FMT_SINGLE and lp == FMT_BF16): + #We can't perform the same exponent operations when hp = lp in single -> bf 16 + #the desired values of the gaurd, round, and sticky bits: + #minSN -3 i_ulp, minSN - 2 i_ulp, minSN - 1 i_ulp, minSN, minSN + 1 i_ulp, minSN + 2 i_ulp, minSN + 3 i_ulp + grs = ['001', '010', '011', '100', '101', '110', '111'] - generate_FP(hp_e_bits, '0', hp_exp, complete_binary_1, hp_e_bias) - generate_FP(hp_e_bits, '1', hp_exp, complete_binary_2, hp_e_bias) - - #MinSN - 1 ulp, MinSN - 2 ulp - for i in range(int('10',2), int('11',2)): - hp_exp = hp_sn_lp_exp - 1 - s = format(i, '0b')#sticky bit - remaining_mantissa_bits = hp_m_bits - 1 - - complete_binary_1 = s + format(random.randint(0, int('1'*remaining_mantissa_bits, 2)), '0b')#randomize the rest of the number - complete_binary_2 = s + format(random.randint(0, int('1'*remaining_mantissa_bits, 2)), '0b') + leading_zeros_len = lp_m_bits - 1 + determined_len = leading_zeros_len + 3 + remaining_rand_len = hp_m_bits - determined_len + max_remaining_rand = int('1'*remaining_rand_len, 2) + + for bits in grs: + remaining_rand_bits_1 = f"{random.randint(0, max_remaining_rand):0{remaining_rand_len}b}" + full_mantissa_1 = bits + remaining_rand_bits_1 + + remaining_rand_bits_2 = f"{random.randint(0, max_remaining_rand):0{remaining_rand_len}b}" + full_mantissa_2 = bits + remaining_rand_bits_2 + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, lp_sn_exp, full_mantissa_1, full_mantissa_2, hp_e_bias, test_f, cover_f) + else: + #MinSN, MinSN + 1 ulp, MinSN + 2 ulp, MinSN + 3 ulp + for i in range(0, int('11', 2) +1): # Iterate over differt round and sticky bits + hp_exp = hp_sn_lp_exp + rs = f"{i:02b}"#round and sticky bit + + remaining_mantissa_bits = hp_m_bits - 2 #find out what part of the mantissa bits left need to be randomized + max_mantissa = int('1'*remaining_mantissa_bits, 2) + + complete_binary_1 = rs + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" + complete_binary_2 = rs + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f) + + #MinSN - 1 ulp, MinSN - 2 ulp + for i in range(0, int('1', 2)+1): + hp_exp = hp_sn_lp_exp - 1 + s = f"{i:01b}"#sticky bit + remaining_mantissa_bits = hp_m_bits - 1 + + max_mantissa = int('1'*remaining_mantissa_bits, 2) + + complete_binary_1 = s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}"#randomize the rest of the number + complete_binary_2 = s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f) + + # #MinSN - 3 ulp + hp_exp = hp_sn_lp_exp - 2 + remaining_mantissa_bits = hp_m_bits + max_mantissa = int('1'*hp_m_bits, 2) + + complete_binary_1 = f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}"#randomize the rest of the number + complete_binary_2 = f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f) - input_1 = generate_FP(hp_e_bits, '0', hp_exp, complete_binary_1, hp_e_bias) - input_2 = generate_FP(hp_e_bits, '1', hp_exp, complete_binary_2, hp_e_bias) +def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): + hp_m_bits = MANTISSA_BITS[hp] + hp_e_bits = EXPONENT_BITS[hp] + hp_e_bias = EXPONENT_BIAS[hp] + lp_n_exp = UNBIASED_EXP[lp][0] + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 #Account for subnorms + lp_m_bits = MANTISSA_BITS[lp] + + rem_bits = hp_m_bits - 2 - lp_m_bits + max_rem = int('1'*rem_bits, 2) + + #MinNorm - 1 i_ulp: + hp_m_1 = '1'*(lp_m_bits) + '1' + '1' + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_m_2 = '1'*(lp_m_bits) + '1' + '1' + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_exp = lp_sn_exp - run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 - run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_2}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 2 + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + + #MinNorm - 2 i_ulp: + hp_m_1 = '1'*(lp_m_bits) + '1' + '1' + '0'*rem_bits + hp_m_2 = '1'*(lp_m_bits) + '1' + '1' + '0'*rem_bits + hp_exp = lp_sn_exp + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + + #MinNorm - 3 i_ulp: + hp_m_1 = '1'*(lp_m_bits) + '1' + '0' + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_m_2 = '1'*(lp_m_bits) + '1' + '0' + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_exp = lp_sn_exp + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + + #MinNorm + 1 i_ulp: + hp_m_1 = '0'*(lp_m_bits - 1) + '0' + '0' + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_m_1 = '0'*(lp_m_bits - 1) + '0' + '0' + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_exp = lp_n_exp + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + + #MinNorm + 2 i_ulp: + hp_m_1 = '0'*(lp_m_bits - 1) + '0' + '1' + '0'*rem_bits + hp_m_1 = '0'*(lp_m_bits - 1) + '0' + '1' + '0'*rem_bits + hp_exp = lp_n_exp + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + + #MinNorm + 3 i_ulp: + hp_m_1 = '0'*(lp_m_bits - 1) + '0' + '1' + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_m_1 = '0'*(lp_m_bits - 1) + '0' + '1' + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_exp = lp_n_exp + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) - #MinSN - 3 ulp - hp_exp = hp_sn_lp_exp - 2 - s = format(i, '0b')#sticky bit - remaining_mantissa_bits = hp_m_bits + + - complete_binary_1 = format(random.randint(0, int('1'*hp_m_bits, 2)), '0b')#randomize the rest of the number - complete_binary_2 = format(random.randint(0, int('1'*hp_m_bits, 2)), '0b') +def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): + hp_m_bits = MANTISSA_BITS[hp] + hp_e_bits = EXPONENT_BITS[hp] + hp_e_bias = EXPONENT_BIAS[hp] + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 #Make the exponent for subnorm values + hp_sn_exp = UNBIASED_EXP[hp][0] - 1 + lp_m_bits = MANTISSA_BITS[lp] - generate_FP(hp_e_bits, '0', hp_exp, complete_binary_1, hp_e_bias) - generate_FP(hp_e_bits, '1', hp_exp, complete_binary_2, hp_e_bias) + #Different for fp_16 and bf_16 because they have the same exponent + if(hp == FMT_SINGLE and lp == FMT_BF16): + hp_exp = hp_sn_exp + leading_zero_bits = lp_m_bits + leading_zeros = '0'*lp_m_bits + remaining_bits = hp_m_bits - lp_m_bits + max_remaining_mantissa = int('1'*remaining_bits, 2) + + remaining_binary_1 = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" + remaining_binary_2 = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" + + complete_binary_1 = leading_zeros + remaining_binary_1 + complete_binary_2 = leading_zeros + remaining_binary_2 + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f) + else: + hp_max_exp = lp_sn_exp - lp_m_bits - 1 #put the hidden 1 of the hp in the rounding bit of the lp + hp_min_exp = hp_sn_exp + max_remaining_mantissa = int('1'*hp_m_bits, 2) + + hp_exp = random.randint(hp_min_exp, hp_max_exp) + complete_binary_1 = f"{random.randint(0, max_remaining_mantissa):0{hp_m_bits}b}" + complete_binary_2 = f"{random.randint(0, max_remaining_mantissa):0{hp_m_bits}b}" + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f) + +def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): + hp_m_bits = MANTISSA_BITS[hp] + hp_e_bits = EXPONENT_BITS[hp] + hp_e_bias = EXPONENT_BIAS[hp] + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 #Account for subnorms + lp_m_bits = MANTISSA_BITS[lp] + max_m_value = int('1'*hp_m_bits, 2) + hp_exp = lp_sn_exp -# def test_conversion_3(lp_min_exp, hp_min_exp, lp_opcode, hp_opcode, rounding_mode, hp_m_bits, hp_e_bits, ): + for i in range(0, 6): + complete_binary = f"{random.randint(0, max_m_value):0{hp_m_bits}b}" + + input_value_1 = generate_FP(hp_e_bits, f"{random.randint(0,1)}", hp_exp, complete_binary, hp_e_bias) + run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 + hp_exp +=1 + def convertTests(test_f, cover_f): #All conversion tests: - for i_hp in range(len(B5_FMTS)): - hp = B5_FMTS[i_hp] - for i_lp in range(i_hp+1, len(B5_FMTS)): - lp = B5_FMTS[i_lp] - for rounding_mode in ROUNDING_MODES: - tests_conversion_1_2(lp, hp, rounding_mode,test_f, cover_f)#Call tests 1 and 2 - + # for i_hp in range(len(B5_FMTS)): + # hp = B5_FMTS[i_hp] + # for i_lp in range(i_hp+1, len(B5_FMTS)): + # lp = B5_FMTS[i_lp] + # for rounding_mode in ROUNDING_MODES: + # tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f)#Call tests 1 and 2 + # tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f)#Call tests 3 and 4 + # tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f)#Call tests 5 and 6 + # tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f)#Call tests 7 and 8 + # tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f)#Call test 9 + + #Testing for Single to Half Precision Converts + hp = FMT_SINGLE + lp = FMT_HALF + rounding_mode = "02" + tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f)#Call tests 5 and 6 + + # tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f)#Call tests 1 and 2 + # tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f)#Call tests 3 and 4 + # tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f)#Call tests 7 and 8 + # tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f)#Call test 9 + From d13c5aec9bdb2b1e174f977c20945cc7df12b4da Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Wed, 4 Mar 2026 19:08:23 -0800 Subject: [PATCH 04/21] precommit checks --- src/cover_float/testgen/B5.py | 62 +++++++++++++++++------------------ 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 6c902aa..30704b9 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -40,15 +40,15 @@ def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): input_1_mantissa = f"{random.randint(0, max_mantissa):0{hp_m_bits}b}" input_2_mantissa = f"{random.randint(0, max_mantissa):0{hp_m_bits}b}" - input_value_1 = generate_FP(hp_e_bits, '0', input_1_exponent, input_1_mantissa, hp_e_bias) - input_value_2 = generate_FP(hp_e_bits, '1', input_2_exponent, input_2_mantissa, hp_e_bias) + input_value_1 = generate_FP(hp_e_bits, "0", input_1_exponent, input_1_mantissa, hp_e_bias) + input_value_2 = generate_FP(hp_e_bits, "1", input_2_exponent, input_2_mantissa, hp_e_bias) run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 2 def genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f): - input_value_1 = generate_FP(hp_e_bits, '0', hp_exp, complete_binary_1, hp_e_bias) - input_value_2 = generate_FP(hp_e_bits, '1', hp_exp, complete_binary_2, hp_e_bias) + input_value_1 = generate_FP(hp_e_bits, "0", hp_exp, complete_binary_1, hp_e_bias) + input_value_2 = generate_FP(hp_e_bits, "1", hp_exp, complete_binary_2, hp_e_bias) run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 2 @@ -57,7 +57,7 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 #Account for subnorms + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] hp_sn_lp_exp = lp_sn_exp - lp_m_bits @@ -98,7 +98,7 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): #MinSN - 1 ulp, MinSN - 2 ulp for i in range(0, int('1', 2)+1): hp_exp = hp_sn_lp_exp - 1 - s = f"{i:01b}"#sticky bit + s = f"{i:01b}" # sticky bit remaining_mantissa_bits = hp_m_bits - 1 max_mantissa = int('1'*remaining_mantissa_bits, 2) @@ -123,7 +123,7 @@ def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] lp_n_exp = UNBIASED_EXP[lp][0] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 #Account for subnorms + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] rem_bits = hp_m_bits - 2 - lp_m_bits @@ -190,29 +190,27 @@ def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): remaining_bits = hp_m_bits - lp_m_bits max_remaining_mantissa = int('1'*remaining_bits, 2) - remaining_binary_1 = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" - remaining_binary_2 = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" + remaining_binary = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" - complete_binary_1 = leading_zeros + remaining_binary_1 - complete_binary_2 = leading_zeros + remaining_binary_2 + complete_binary_1 = leading_zeros + remaining_binary - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f) + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary, complete_binary, hp_e_bias, test_f, cover_f) else: hp_max_exp = lp_sn_exp - lp_m_bits - 1 #put the hidden 1 of the hp in the rounding bit of the lp hp_min_exp = hp_sn_exp max_remaining_mantissa = int('1'*hp_m_bits, 2) hp_exp = random.randint(hp_min_exp, hp_max_exp) - complete_binary_1 = f"{random.randint(0, max_remaining_mantissa):0{hp_m_bits}b}" - complete_binary_2 = f"{random.randint(0, max_remaining_mantissa):0{hp_m_bits}b}" - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f) + complete_binary = f"{random.randint(0, max_remaining_mantissa):0{hp_m_bits}b}" + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary, complete_binary, hp_e_bias, test_f, cover_f) def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 #Account for subnorms + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] max_m_value = int('1'*hp_m_bits, 2) @@ -227,23 +225,23 @@ def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): def convertTests(test_f, cover_f): - #All conversion tests: - # for i_hp in range(len(B5_FMTS)): - # hp = B5_FMTS[i_hp] - # for i_lp in range(i_hp+1, len(B5_FMTS)): - # lp = B5_FMTS[i_lp] - # for rounding_mode in ROUNDING_MODES: - # tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f)#Call tests 1 and 2 - # tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f)#Call tests 3 and 4 - # tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f)#Call tests 5 and 6 - # tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f)#Call tests 7 and 8 - # tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f)#Call test 9 + # All conversion tests: + for i_hp in range(len(B5_FMTS)): + hp = B5_FMTS[i_hp] + for i_lp in range(i_hp + 1, len(B5_FMTS)): + lp = B5_FMTS[i_lp] + for rounding_mode in ROUNDING_MODES: + tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f)#Call tests 1 and 2 + tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f)#Call tests 3 and 4 + tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f)#Call tests 5 and 6 + tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f)#Call tests 7 and 8 + tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f)#Call test 9 #Testing for Single to Half Precision Converts - hp = FMT_SINGLE - lp = FMT_HALF - rounding_mode = "02" - tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f)#Call tests 5 and 6 + # hp = FMT_SINGLE + # lp = FMT_HALF + # rounding_mode = "01" + # tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f)#Call tests 5 and 6 # tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f)#Call tests 1 and 2 # tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f)#Call tests 3 and 4 @@ -260,4 +258,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() From c98b5726f0d14b479d32e1cedc5c764b131b6908 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Wed, 4 Mar 2026 19:25:55 -0800 Subject: [PATCH 05/21] more precommit checks --- src/cover_float/common/constants.py | 17 +--- src/cover_float/testgen/B5.py | 117 ++++++++++++++++------------ 2 files changed, 70 insertions(+), 64 deletions(-) diff --git a/src/cover_float/common/constants.py b/src/cover_float/common/constants.py index d18e9fd..a288569 100644 --- a/src/cover_float/common/constants.py +++ b/src/cover_float/common/constants.py @@ -66,22 +66,9 @@ FMT_BF16 : [-126, 127] } -EXPONENT_BIAS = { - FMT_HALF : 15, - FMT_SINGLE : 127, - FMT_DOUBLE : 1023, - FMT_QUAD : 16383, - FMT_BF16 : 127 -} +EXPONENT_BIAS = {FMT_HALF: 15, FMT_SINGLE: 127, FMT_DOUBLE: 1023, FMT_QUAD: 16383, FMT_BF16: 127} -ROUNDING_MODES = { - ROUND_NEAR_EVEN, - ROUND_MINMAG, - ROUND_MIN, - ROUND_MAX, - ROUND_NEAR_MAXMAG, - ROUND_ODD -} +ROUNDING_MODES = {ROUND_NEAR_EVEN, ROUND_MINMAG, ROUND_MIN, ROUND_MAX, ROUND_NEAR_MAXMAG, ROUND_ODD} MANTISSA_BITS = {FMT_HALF: 10, FMT_SINGLE: 23, FMT_DOUBLE: 52, FMT_QUAD: 112, FMT_BF16: 7} diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 30704b9..113dd5d 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -1,11 +1,22 @@ -#Lamarr -#B5 Model +# Lamarr +# B5 Model import random +from cover_float.common.constants import ( + EXPONENT_BIAS, + EXPONENT_BITS, + FMT_BF16, + FMT_DOUBLE, + FMT_HALF, + FMT_QUAD, + FMT_SINGLE, + MANTISSA_BITS, + OP_CFF, + ROUNDING_MODES, + UNBIASED_EXP, +) from cover_float.reference import run_and_store_test_vector -from cover_float.common.constants import UNBIASED_EXP, OP_CFF, FMT_QUAD, FMT_DOUBLE, FMT_SINGLE, FMT_BF16, FMT_HALF, ROUNDING_MODES, MANTISSA_BITS, ROUNDING_MODES, EXPONENT_BITS, EXPONENT_BIAS - B5_FMTS = [FMT_QUAD, FMT_DOUBLE, FMT_SINGLE, FMT_BF16, FMT_HALF] @@ -22,20 +33,20 @@ def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 #Make the exponent for subnorm values + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Make the exponent for subnorm values lp_m_bits = MANTISSA_BITS[lp] hp_max_exp = lp_sn_exp - 1 hp_min_exp = lp_sn_exp - lp_m_bits - if(hp == FMT_SINGLE and lp == FMT_BF16): #Different case for FP_32 and BF_16 + if hp == FMT_SINGLE and lp == FMT_BF16: # Different case for FP_32 and BF_16 hp_min_exp = lp_sn_exp hp_max_exp = lp_sn_exp input_1_exponent = random.randint(hp_min_exp, hp_max_exp) input_2_exponent = random.randint(hp_min_exp, hp_max_exp) - max_mantissa = int('1'* hp_m_bits, 2) + max_mantissa = int("1"* hp_m_bits, 2) input_1_mantissa = f"{random.randint(0, max_mantissa):0{hp_m_bits}b}" input_2_mantissa = f"{random.randint(0, max_mantissa):0{hp_m_bits}b}" @@ -57,7 +68,7 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] hp_sn_lp_exp = lp_sn_exp - lp_m_bits @@ -71,7 +82,7 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): leading_zeros_len = lp_m_bits - 1 determined_len = leading_zeros_len + 3 remaining_rand_len = hp_m_bits - determined_len - max_remaining_rand = int('1'*remaining_rand_len, 2) + max_remaining_rand = int("1" * remaining_rand_len, 2) for bits in grs: remaining_rand_bits_1 = f"{random.randint(0, max_remaining_rand):0{remaining_rand_len}b}" @@ -80,15 +91,28 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): remaining_rand_bits_2 = f"{random.randint(0, max_remaining_rand):0{remaining_rand_len}b}" full_mantissa_2 = bits + remaining_rand_bits_2 - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, lp_sn_exp, full_mantissa_1, full_mantissa_2, hp_e_bias, test_f, cover_f) + genTestVectors3_4( + lp, + hp, + rounding_mode, + hp_e_bits, + lp_sn_exp, + full_mantissa_1, + full_mantissa_2, + hp_e_bias, + test_f, + cover_f, + ) else: - #MinSN, MinSN + 1 ulp, MinSN + 2 ulp, MinSN + 3 ulp - for i in range(0, int('11', 2) +1): # Iterate over differt round and sticky bits + # MinSN, MinSN + 1 ulp, MinSN + 2 ulp, MinSN + 3 ulp + for i in range(0, int("11", 2) +1): # Iterate over differt round and sticky bits hp_exp = hp_sn_lp_exp - rs = f"{i:02b}"#round and sticky bit + rs = f"{i:02b}" # round and sticky bit - remaining_mantissa_bits = hp_m_bits - 2 #find out what part of the mantissa bits left need to be randomized - max_mantissa = int('1'*remaining_mantissa_bits, 2) + remaining_mantissa_bits = ( + hp_m_bits - 2 + ) # find out what part of the mantissa bits left need to be randomized + max_mantissa = int("1" * remaining_mantissa_bits, 2) complete_binary_1 = rs + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" complete_binary_2 = rs + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" @@ -123,53 +147,48 @@ def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] lp_n_exp = UNBIASED_EXP[lp][0] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] rem_bits = hp_m_bits - 2 - lp_m_bits - max_rem = int('1'*rem_bits, 2) + max_rem = int("1" * rem_bits, 2) #MinNorm - 1 i_ulp: - hp_m_1 = '1'*(lp_m_bits) + '1' + '1' + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_m_2 = '1'*(lp_m_bits) + '1' + '1' + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_m_1 = "1" * (lp_m_bits) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_m_2 = "1" * (lp_m_bits) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_sn_exp genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) #MinNorm - 2 i_ulp: - hp_m_1 = '1'*(lp_m_bits) + '1' + '1' + '0'*rem_bits - hp_m_2 = '1'*(lp_m_bits) + '1' + '1' + '0'*rem_bits + hp_m = "1" * (lp_m_bits) + "1" + "1" + "0" * rem_bits hp_exp = lp_sn_exp - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) - #MinNorm - 3 i_ulp: - hp_m_1 = '1'*(lp_m_bits) + '1' + '0' + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_m_2 = '1'*(lp_m_bits) + '1' + '0' + f"{random.randint(1, max_rem):0{rem_bits}b}" + # MinNorm - 3 i_ulp: + hp_m = "1" * (lp_m_bits) + "1" + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_sn_exp - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) - #MinNorm + 1 i_ulp: - hp_m_1 = '0'*(lp_m_bits - 1) + '0' + '0' + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_m_1 = '0'*(lp_m_bits - 1) + '0' + '0' + f"{random.randint(1, max_rem):0{rem_bits}b}" + # MinNorm + 1 i_ulp: + hp_m = "0" * (lp_m_bits - 1) + "0" + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_n_exp - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) - #MinNorm + 2 i_ulp: - hp_m_1 = '0'*(lp_m_bits - 1) + '0' + '1' + '0'*rem_bits - hp_m_1 = '0'*(lp_m_bits - 1) + '0' + '1' + '0'*rem_bits + # MinNorm + 2 i_ulp: + hp_m = "0" * (lp_m_bits - 1) + "0" + "1" + "0" * rem_bits hp_exp = lp_n_exp - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) - #MinNorm + 3 i_ulp: - hp_m_1 = '0'*(lp_m_bits - 1) + '0' + '1' + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_m_1 = '0'*(lp_m_bits - 1) + '0' + '1' + f"{random.randint(1, max_rem):0{rem_bits}b}" + # MinNorm + 3 i_ulp: + hp_m = "0" * (lp_m_bits - 1) + "0" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_n_exp - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) @@ -178,17 +197,17 @@ def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 #Make the exponent for subnorm values + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Make the exponent for subnorm values hp_sn_exp = UNBIASED_EXP[hp][0] - 1 lp_m_bits = MANTISSA_BITS[lp] - #Different for fp_16 and bf_16 because they have the same exponent - if(hp == FMT_SINGLE and lp == FMT_BF16): + # Different for fp_16 and bf_16 because they have the same exponent + if hp == FMT_SINGLE and lp == FMT_BF16: hp_exp = hp_sn_exp leading_zero_bits = lp_m_bits - leading_zeros = '0'*lp_m_bits + leading_zeros = "0" * lp_m_bits remaining_bits = hp_m_bits - lp_m_bits - max_remaining_mantissa = int('1'*remaining_bits, 2) + max_remaining_mantissa = int("1" * remaining_bits, 2) remaining_binary = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" @@ -210,7 +229,7 @@ def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] max_m_value = int('1'*hp_m_bits, 2) @@ -231,11 +250,11 @@ def convertTests(test_f, cover_f): for i_lp in range(i_hp + 1, len(B5_FMTS)): lp = B5_FMTS[i_lp] for rounding_mode in ROUNDING_MODES: - tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f)#Call tests 1 and 2 - tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f)#Call tests 3 and 4 - tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f)#Call tests 5 and 6 - tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f)#Call tests 7 and 8 - tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f)#Call test 9 + tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f) + tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f) + tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f) + tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f) + tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f) #Testing for Single to Half Precision Converts # hp = FMT_SINGLE From 99bf0969be8fc7253399bf4042951c434e57861c Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Thu, 5 Mar 2026 11:36:12 -0800 Subject: [PATCH 06/21] adjusting B5 tests 5 and 6 --- src/cover_float/common/constants.py | 2 +- src/cover_float/testgen/B5.py | 113 +++++++++++++--------------- 2 files changed, 54 insertions(+), 61 deletions(-) diff --git a/src/cover_float/common/constants.py b/src/cover_float/common/constants.py index a288569..300f0f4 100644 --- a/src/cover_float/common/constants.py +++ b/src/cover_float/common/constants.py @@ -63,7 +63,7 @@ FMT_SINGLE : [-126, 127], FMT_DOUBLE : [-1022, 1023], FMT_QUAD : [-16382, 16383], - FMT_BF16 : [-126, 127] + FMT_BF16 : [-126, 127], } EXPONENT_BIAS = {FMT_HALF: 15, FMT_SINGLE: 127, FMT_DOUBLE: 1023, FMT_QUAD: 16383, FMT_BF16: 127} diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 113dd5d..6d53ae7 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -39,14 +39,14 @@ def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): hp_max_exp = lp_sn_exp - 1 hp_min_exp = lp_sn_exp - lp_m_bits - if hp == FMT_SINGLE and lp == FMT_BF16: # Different case for FP_32 and BF_16 + if hp == FMT_SINGLE and lp == FMT_BF16: # Different case for FP_32 and BF_16 hp_min_exp = lp_sn_exp hp_max_exp = lp_sn_exp input_1_exponent = random.randint(hp_min_exp, hp_max_exp) input_2_exponent = random.randint(hp_min_exp, hp_max_exp) - max_mantissa = int("1"* hp_m_bits, 2) + max_mantissa = int("1" * hp_m_bits, 2) input_1_mantissa = f"{random.randint(0, max_mantissa):0{hp_m_bits}b}" input_2_mantissa = f"{random.randint(0, max_mantissa):0{hp_m_bits}b}" @@ -135,7 +135,7 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): # #MinSN - 3 ulp hp_exp = hp_sn_lp_exp - 2 remaining_mantissa_bits = hp_m_bits - max_mantissa = int('1'*hp_m_bits, 2) + max_mantissa = int('1' * hp_m_bits, 2) complete_binary_1 = f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}"#randomize the rest of the number complete_binary_2 = f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" @@ -150,45 +150,50 @@ def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] - rem_bits = hp_m_bits - 2 - lp_m_bits - max_rem = int("1" * rem_bits, 2) - - #MinNorm - 1 i_ulp: - hp_m_1 = "1" * (lp_m_bits) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_m_2 = "1" * (lp_m_bits) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_exp = lp_sn_exp - - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + if(hp != FMT_BF16 and lp != FMT_SINGLE): # The mantissa bits for bf_16 are smaller than that for single, so you can't do these operations + rem_bits = hp_m_bits + 1 - 2 - lp_m_bits - #MinNorm - 2 i_ulp: - hp_m = "1" * (lp_m_bits) + "1" + "1" + "0" * rem_bits - hp_exp = lp_sn_exp - - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) - - # MinNorm - 3 i_ulp: - hp_m = "1" * (lp_m_bits) + "1" + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_exp = lp_sn_exp - - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) - - # MinNorm + 1 i_ulp: - hp_m = "0" * (lp_m_bits - 1) + "0" + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_exp = lp_n_exp - - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) - - # MinNorm + 2 i_ulp: - hp_m = "0" * (lp_m_bits - 1) + "0" + "1" + "0" * rem_bits - hp_exp = lp_n_exp - - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) - - # MinNorm + 3 i_ulp: - hp_m = "0" * (lp_m_bits - 1) + "0" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_exp = lp_n_exp - - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + max_rem = int("1" * rem_bits, 2) + + #MinNorm - 1 i_ulp: + hp_m_1 = "1" * (lp_m_bits-1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_m_2 = "1" * (lp_m_bits-1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_exp = lp_sn_exp + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + + #MinNorm - 2 i_ulp: + hp_m = "1" * (lp_m_bits - 1) + "1" + "1" + "0" * rem_bits + hp_exp = lp_sn_exp + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + + # MinNorm - 3 i_ulp: + hp_m = "1" * (lp_m_bits - 1) + "1" + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_exp = lp_sn_exp + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + + # MinNorm + 1 i_ulp: + hp_m = "0" * (lp_m_bits - 1) + "0" + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_exp = lp_n_exp + print(hp_m) + print("0"*lp_m_bits) + print(len(hp_m), hp_m_bits) + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + + # MinNorm + 2 i_ulp: + hp_m = "0" * (lp_m_bits - 1) + "0" + "1" + "0" * rem_bits + hp_exp = lp_n_exp + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + + # MinNorm + 3 i_ulp: + hp_m = "0" * (lp_m_bits - 1) + "0" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_exp = lp_n_exp + + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) @@ -209,15 +214,17 @@ def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): remaining_bits = hp_m_bits - lp_m_bits max_remaining_mantissa = int("1" * remaining_bits, 2) - remaining_binary = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" + remaining_binary_1 = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" + remaining_binary_2 = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" - complete_binary_1 = leading_zeros + remaining_binary + complete_binary_1 = leading_zeros + remaining_binary_1 + complete_binary_2 = leading_zeros + remaining_binary_2 - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary, complete_binary, hp_e_bias, test_f, cover_f) + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f) else: hp_max_exp = lp_sn_exp - lp_m_bits - 1 #put the hidden 1 of the hp in the rounding bit of the lp hp_min_exp = hp_sn_exp - max_remaining_mantissa = int('1'*hp_m_bits, 2) + max_remaining_mantissa = int("1" * hp_m_bits, 2) hp_exp = random.randint(hp_min_exp, hp_max_exp) @@ -231,7 +238,7 @@ def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): hp_e_bias = EXPONENT_BIAS[hp] lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] - max_m_value = int('1'*hp_m_bits, 2) + max_m_value = int("1" * hp_m_bits, 2) hp_exp = lp_sn_exp @@ -255,20 +262,6 @@ def convertTests(test_f, cover_f): tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f) tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f) tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f) - - #Testing for Single to Half Precision Converts - # hp = FMT_SINGLE - # lp = FMT_HALF - # rounding_mode = "01" - # tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f)#Call tests 5 and 6 - - # tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f)#Call tests 1 and 2 - # tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f)#Call tests 3 and 4 - # tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f)#Call tests 7 and 8 - # tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f)#Call test 9 - - - def main(): From 17978871fc39ec5223b1fd600129853f27c04fcd Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Thu, 5 Mar 2026 13:04:57 -0800 Subject: [PATCH 07/21] precommit checks --- src/cover_float/testgen/B5.py | 93 +++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 6d53ae7..af843d6 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -73,11 +73,11 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): hp_sn_lp_exp = lp_sn_exp - lp_m_bits - if(hp == FMT_SINGLE and lp == FMT_BF16): - #We can't perform the same exponent operations when hp = lp in single -> bf 16 - #the desired values of the gaurd, round, and sticky bits: - #minSN -3 i_ulp, minSN - 2 i_ulp, minSN - 1 i_ulp, minSN, minSN + 1 i_ulp, minSN + 2 i_ulp, minSN + 3 i_ulp - grs = ['001', '010', '011', '100', '101', '110', '111'] + if hp == FMT_SINGLE and lp == FMT_BF16: + # We can't perform the same exponent operations when hp = lp in single -> bf 16 + # the desired values of the gaurd, round, and sticky bits: + # minSN -3 i_ulp, minSN - 2 i_ulp, minSN - 1 i_ulp, minSN, minSN + 1 i_ulp, minSN + 2 i_ulp, minSN + 3 i_ulp + grs = ["001", "010", "011", "100", "101", "110", "111"] leading_zeros_len = lp_m_bits - 1 determined_len = leading_zeros_len + 3 @@ -105,7 +105,7 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): ) else: # MinSN, MinSN + 1 ulp, MinSN + 2 ulp, MinSN + 3 ulp - for i in range(0, int("11", 2) +1): # Iterate over differt round and sticky bits + for i in range(0, int("11", 2) + 1): # Iterate over differt round and sticky bits hp_exp = hp_sn_lp_exp rs = f"{i:02b}" # round and sticky bit @@ -117,31 +117,56 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): complete_binary_1 = rs + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" complete_binary_2 = rs + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f) + genTestVectors3_4( + lp, + hp, + rounding_mode, + hp_e_bits, + hp_exp, + complete_binary_1, + complete_binary_2, + hp_e_bias, + test_f, + cover_f, + ) - #MinSN - 1 ulp, MinSN - 2 ulp - for i in range(0, int('1', 2)+1): + # MinSN - 1 ulp, MinSN - 2 ulp + for i in range(0, int("1", 2) + 1): hp_exp = hp_sn_lp_exp - 1 - s = f"{i:01b}" # sticky bit + s = f"{i:01b}" # sticky bit remaining_mantissa_bits = hp_m_bits - 1 - max_mantissa = int('1'*remaining_mantissa_bits, 2) + max_mantissa = int("1" * remaining_mantissa_bits, 2) complete_binary_1 = s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}"#randomize the rest of the number complete_binary_2 = s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f) + genTestVectors3_4( + lp, + hp, + rounding_mode, + hp_e_bits, + hp_exp, + complete_binary_1, + complete_binary_2, + hp_e_bias, + test_f, + cover_f, + ) # #MinSN - 3 ulp hp_exp = hp_sn_lp_exp - 2 remaining_mantissa_bits = hp_m_bits - max_mantissa = int('1' * hp_m_bits, 2) + max_mantissa = int("1" * hp_m_bits, 2) - complete_binary_1 = f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}"#randomize the rest of the number + complete_binary_1 = ( + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" # randomize the rest of the number + ) complete_binary_2 = f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f) - + genTestVectors3_4( + lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f + ) def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] @@ -150,19 +175,21 @@ def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] - if(hp != FMT_BF16 and lp != FMT_SINGLE): # The mantissa bits for bf_16 are smaller than that for single, so you can't do these operations + if ( + hp != FMT_BF16 and lp != FMT_SINGLE + ): # The mantissa bits for bf_16 are smaller than that for single, so you can't do these operations rem_bits = hp_m_bits + 1 - 2 - lp_m_bits max_rem = int("1" * rem_bits, 2) #MinNorm - 1 i_ulp: - hp_m_1 = "1" * (lp_m_bits-1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_m_2 = "1" * (lp_m_bits-1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_m_1 = "1" * (lp_m_bits - 1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_m_2 = "1" * (lp_m_bits - 1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_sn_exp genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) - #MinNorm - 2 i_ulp: + # MinNorm - 2 i_ulp: hp_m = "1" * (lp_m_bits - 1) + "1" + "1" + "0" * rem_bits hp_exp = lp_sn_exp @@ -177,9 +204,6 @@ def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): # MinNorm + 1 i_ulp: hp_m = "0" * (lp_m_bits - 1) + "0" + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_n_exp - print(hp_m) - print("0"*lp_m_bits) - print(len(hp_m), hp_m_bits) genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) @@ -202,7 +226,7 @@ def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Make the exponent for subnorm values + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 hp_sn_exp = UNBIASED_EXP[hp][0] - 1 lp_m_bits = MANTISSA_BITS[lp] @@ -212,7 +236,7 @@ def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): leading_zero_bits = lp_m_bits leading_zeros = "0" * lp_m_bits remaining_bits = hp_m_bits - lp_m_bits - max_remaining_mantissa = int("1" * remaining_bits, 2) + max_remaining_mantissa = int("1" * remaining_bits, 2) remaining_binary_1 = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" remaining_binary_2 = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" @@ -220,9 +244,11 @@ def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): complete_binary_1 = leading_zeros + remaining_binary_1 complete_binary_2 = leading_zeros + remaining_binary_2 - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f) + genTestVectors3_4( + lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f + ) else: - hp_max_exp = lp_sn_exp - lp_m_bits - 1 #put the hidden 1 of the hp in the rounding bit of the lp + hp_max_exp = lp_sn_exp - lp_m_bits - 1 # put the hidden 1 of the hp in the rounding bit of the lp hp_min_exp = hp_sn_exp max_remaining_mantissa = int("1" * hp_m_bits, 2) @@ -230,8 +256,9 @@ def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): complete_binary = f"{random.randint(0, max_remaining_mantissa):0{hp_m_bits}b}" - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary, complete_binary, hp_e_bias, test_f, cover_f) - + genTestVectors3_4( + lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary, complete_binary, hp_e_bias, test_f, cover_f + ) def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] @@ -245,9 +272,11 @@ def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): for i in range(0, 6): complete_binary = f"{random.randint(0, max_m_value):0{hp_m_bits}b}" - input_value_1 = generate_FP(hp_e_bits, f"{random.randint(0,1)}", hp_exp, complete_binary, hp_e_bias) - run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 - hp_exp +=1 + input_value_1 = generate_FP(hp_e_bits, f"{random.randint(0, 1)}", hp_exp, complete_binary, hp_e_bias) + run_and_store_test_vector( + f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f + ) # Test 1 + hp_exp += 1 def convertTests(test_f, cover_f): From f359566c12025f8d3f256804365fceb6da52c1c1 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Thu, 5 Mar 2026 13:11:21 -0800 Subject: [PATCH 08/21] precommit chceks --- src/cover_float/common/constants.py | 10 +++++----- src/cover_float/testgen/B5.py | 13 ++++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/cover_float/common/constants.py b/src/cover_float/common/constants.py index 300f0f4..f3f15db 100644 --- a/src/cover_float/common/constants.py +++ b/src/cover_float/common/constants.py @@ -59,11 +59,11 @@ # Defining floating point format constants UNBIASED_EXP = { - FMT_HALF : [-14, 15], - FMT_SINGLE : [-126, 127], - FMT_DOUBLE : [-1022, 1023], - FMT_QUAD : [-16382, 16383], - FMT_BF16 : [-126, 127], + FMT_HALF: [-14, 15], + FMT_SINGLE: [-126, 127], + FMT_DOUBLE: [-1022, 1023], + FMT_QUAD: [-16382, 16383], + FMT_BF16: [-126, 127], } EXPONENT_BIAS = {FMT_HALF: 15, FMT_SINGLE: 127, FMT_DOUBLE: 1023, FMT_QUAD: 16383, FMT_BF16: 127} diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index af843d6..5235512 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -57,7 +57,9 @@ def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 2 -def genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f): +def genTestVectors3_4( + lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f +): input_value_1 = generate_FP(hp_e_bits, "0", hp_exp, complete_binary_1, hp_e_bias) input_value_2 = generate_FP(hp_e_bits, "1", hp_exp, complete_binary_2, hp_e_bias) @@ -138,8 +140,9 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): max_mantissa = int("1" * remaining_mantissa_bits, 2) - complete_binary_1 = s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}"#randomize the rest of the number - complete_binary_2 = s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" + complete_binary_1 = ( + s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" + ) # randomize the rest of the number complete_binary_2 = s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" genTestVectors3_4( lp, @@ -182,7 +185,7 @@ def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): max_rem = int("1" * rem_bits, 2) - #MinNorm - 1 i_ulp: + # MinNorm - 1 i_ulp: hp_m_1 = "1" * (lp_m_bits - 1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_m_2 = "1" * (lp_m_bits - 1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_sn_exp @@ -245,7 +248,7 @@ def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): complete_binary_2 = leading_zeros + remaining_binary_2 genTestVectors3_4( - lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f + lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f ) else: hp_max_exp = lp_sn_exp - lp_m_bits - 1 # put the hidden 1 of the hp in the rounding bit of the lp From 65b347b454f78fddaaadf002c4b35726ae11f2c7 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Thu, 5 Mar 2026 13:14:24 -0800 Subject: [PATCH 09/21] precommit checks --- src/cover_float/testgen/B5.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 5235512..fe7aee5 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -54,8 +54,12 @@ def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): input_value_1 = generate_FP(hp_e_bits, "0", input_1_exponent, input_1_mantissa, hp_e_bias) input_value_2 = generate_FP(hp_e_bits, "1", input_2_exponent, input_2_mantissa, hp_e_bias) - run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 - run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 2 + run_and_store_test_vector( + f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f + ) # Test 1 + run_and_store_test_vector( + f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f + ) # Test 2 def genTestVectors3_4( lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f @@ -63,8 +67,12 @@ def genTestVectors3_4( input_value_1 = generate_FP(hp_e_bits, "0", hp_exp, complete_binary_1, hp_e_bias) input_value_2 = generate_FP(hp_e_bits, "1", hp_exp, complete_binary_2, hp_e_bias) - run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 1 - run_and_store_test_vector(f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32*'0'}_{32*'0'}_{hp}_{32*'0'}_{lp}_00", test_f, cover_f) #Test 2 + run_and_store_test_vector( + f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f + ) # Test 1 + run_and_store_test_vector( + f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f + ) # Test 2 def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] @@ -272,7 +280,7 @@ def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): hp_exp = lp_sn_exp - for i in range(0, 6): + for i in range(0, 6): complete_binary = f"{random.randint(0, max_m_value):0{hp_m_bits}b}" input_value_1 = generate_FP(hp_e_bits, f"{random.randint(0, 1)}", hp_exp, complete_binary, hp_e_bias) From 16de2e018638b9ea6686b2bc36d5c4d37709a957 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Thu, 5 Mar 2026 13:20:28 -0800 Subject: [PATCH 10/21] removing whitespace --- src/cover_float/testgen/B5.py | 57 ++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index fe7aee5..bffa192 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -3,6 +3,7 @@ import random + from cover_float.common.constants import ( EXPONENT_BIAS, EXPONENT_BITS, @@ -25,7 +26,7 @@ def generate_FP(input_e_bitwidth, input_sign, input_exponent, input_mantissa, in exponent = f"{input_exponent + input_bias:0{input_e_bitwidth}b}" complete = input_sign + exponent + input_mantissa fp_complete = format(int(complete, 2), "X") - + return fp_complete @@ -45,50 +46,52 @@ def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): input_1_exponent = random.randint(hp_min_exp, hp_max_exp) input_2_exponent = random.randint(hp_min_exp, hp_max_exp) - + max_mantissa = int("1" * hp_m_bits, 2) - + input_1_mantissa = f"{random.randint(0, max_mantissa):0{hp_m_bits}b}" input_2_mantissa = f"{random.randint(0, max_mantissa):0{hp_m_bits}b}" - + input_value_1 = generate_FP(hp_e_bits, "0", input_1_exponent, input_1_mantissa, hp_e_bias) input_value_2 = generate_FP(hp_e_bits, "1", input_2_exponent, input_2_mantissa, hp_e_bias) - + run_and_store_test_vector( f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f ) # Test 1 run_and_store_test_vector( f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f ) # Test 2 - + + def genTestVectors3_4( lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f ): input_value_1 = generate_FP(hp_e_bits, "0", hp_exp, complete_binary_1, hp_e_bias) input_value_2 = generate_FP(hp_e_bits, "1", hp_exp, complete_binary_2, hp_e_bias) - + run_and_store_test_vector( f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f ) # Test 1 run_and_store_test_vector( f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f ) # Test 2 - + + def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] - + hp_sn_lp_exp = lp_sn_exp - lp_m_bits - + if hp == FMT_SINGLE and lp == FMT_BF16: # We can't perform the same exponent operations when hp = lp in single -> bf 16 # the desired values of the gaurd, round, and sticky bits: # minSN -3 i_ulp, minSN - 2 i_ulp, minSN - 1 i_ulp, minSN, minSN + 1 i_ulp, minSN + 2 i_ulp, minSN + 3 i_ulp grs = ["001", "010", "011", "100", "101", "110", "111"] - + leading_zeros_len = lp_m_bits - 1 determined_len = leading_zeros_len + 3 remaining_rand_len = hp_m_bits - determined_len @@ -97,7 +100,7 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): for bits in grs: remaining_rand_bits_1 = f"{random.randint(0, max_remaining_rand):0{remaining_rand_len}b}" full_mantissa_1 = bits + remaining_rand_bits_1 - + remaining_rand_bits_2 = f"{random.randint(0, max_remaining_rand):0{remaining_rand_len}b}" full_mantissa_2 = bits + remaining_rand_bits_2 @@ -118,15 +121,15 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): for i in range(0, int("11", 2) + 1): # Iterate over differt round and sticky bits hp_exp = hp_sn_lp_exp rs = f"{i:02b}" # round and sticky bit - + remaining_mantissa_bits = ( hp_m_bits - 2 ) # find out what part of the mantissa bits left need to be randomized max_mantissa = int("1" * remaining_mantissa_bits, 2) - + complete_binary_1 = rs + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" complete_binary_2 = rs + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" - + genTestVectors3_4( lp, hp, @@ -229,9 +232,7 @@ def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): hp_exp = lp_n_exp genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) - - - + def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] @@ -248,13 +249,13 @@ def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): leading_zeros = "0" * lp_m_bits remaining_bits = hp_m_bits - lp_m_bits max_remaining_mantissa = int("1" * remaining_bits, 2) - + remaining_binary_1 = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" remaining_binary_2 = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" - + complete_binary_1 = leading_zeros + remaining_binary_1 complete_binary_2 = leading_zeros + remaining_binary_2 - + genTestVectors3_4( lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f ) @@ -262,14 +263,16 @@ def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): hp_max_exp = lp_sn_exp - lp_m_bits - 1 # put the hidden 1 of the hp in the rounding bit of the lp hp_min_exp = hp_sn_exp max_remaining_mantissa = int("1" * hp_m_bits, 2) - + hp_exp = random.randint(hp_min_exp, hp_max_exp) - + complete_binary = f"{random.randint(0, max_remaining_mantissa):0{hp_m_bits}b}" - + genTestVectors3_4( lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary, complete_binary, hp_e_bias, test_f, cover_f ) + + def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] @@ -277,9 +280,9 @@ def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] max_m_value = int("1" * hp_m_bits, 2) - + hp_exp = lp_sn_exp - + for i in range(0, 6): complete_binary = f"{random.randint(0, max_m_value):0{hp_m_bits}b}" @@ -288,7 +291,7 @@ def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f ) # Test 1 hp_exp += 1 - + def convertTests(test_f, cover_f): # All conversion tests: From 76cd65ce8f60325490d1caa4fd26f8cfc7086396 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Thu, 5 Mar 2026 13:22:43 -0800 Subject: [PATCH 11/21] whitespace --- src/cover_float/testgen/B5.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index bffa192..728e801 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -167,7 +167,7 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): test_f, cover_f, ) - + # #MinSN - 3 ulp hp_exp = hp_sn_lp_exp - 2 remaining_mantissa_bits = hp_m_bits @@ -181,6 +181,8 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): genTestVectors3_4( lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f ) + + def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] @@ -188,7 +190,7 @@ def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): lp_n_exp = UNBIASED_EXP[lp][0] lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] - + if ( hp != FMT_BF16 and lp != FMT_SINGLE ): # The mantissa bits for bf_16 are smaller than that for single, so you can't do these operations @@ -200,7 +202,7 @@ def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): hp_m_1 = "1" * (lp_m_bits - 1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_m_2 = "1" * (lp_m_bits - 1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_sn_exp - + genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) # MinNorm - 2 i_ulp: From f3399b0875f433873caf00c8b9b275af0ab0c2a6 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Thu, 5 Mar 2026 13:24:32 -0800 Subject: [PATCH 12/21] whitespace --- src/cover_float/testgen/B5.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 728e801..ef3f2eb 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -103,7 +103,7 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): remaining_rand_bits_2 = f"{random.randint(0, max_remaining_rand):0{remaining_rand_len}b}" full_mantissa_2 = bits + remaining_rand_bits_2 - + genTestVectors3_4( lp, hp, @@ -148,7 +148,7 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): hp_exp = hp_sn_lp_exp - 1 s = f"{i:01b}" # sticky bit remaining_mantissa_bits = hp_m_bits - 1 - + max_mantissa = int("1" * remaining_mantissa_bits, 2) complete_binary_1 = ( From 69d69e9d865db4f767c6034b388c784030c8f988 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Thu, 5 Mar 2026 15:12:18 -0800 Subject: [PATCH 13/21] added type annotation --- src/cover_float/testgen/B5.py | 89 +++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 26 deletions(-) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index ef3f2eb..9ab6fdb 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -3,6 +3,7 @@ import random +from typing import TextIO from cover_float.common.constants import ( EXPONENT_BIAS, @@ -22,7 +23,13 @@ B5_FMTS = [FMT_QUAD, FMT_DOUBLE, FMT_SINGLE, FMT_BF16, FMT_HALF] -def generate_FP(input_e_bitwidth, input_sign, input_exponent, input_mantissa, input_bias): +def generate_FP( + input_e_bitwidth: int, + input_sign: str, + input_exponent: int, + input_mantissa: str, + input_bias: int + ) -> str: exponent = f"{input_exponent + input_bias:0{input_e_bitwidth}b}" complete = input_sign + exponent + input_mantissa fp_complete = format(int(complete, 2), "X") @@ -30,7 +37,13 @@ def generate_FP(input_e_bitwidth, input_sign, input_exponent, input_mantissa, in return fp_complete -def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): +def tests_conversion_1_2( + lp: str, + hp: str, + rounding_mode: str, + test_f: TextIO, + cover_f: TextIO + ) -> None: hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] @@ -63,9 +76,18 @@ def tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f): ) # Test 2 -def genTestVectors3_4( - lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f -): +def genPNTestVectors( + lp: str, + hp: str, + rounding_mode: str, + hp_e_bits: int, + hp_exp: int, + complete_binary_1: str, + complete_binary_2: str, + hp_e_bias: int, + test_f: str, + cover_f: str +) -> None: input_value_1 = generate_FP(hp_e_bits, "0", hp_exp, complete_binary_1, hp_e_bias) input_value_2 = generate_FP(hp_e_bits, "1", hp_exp, complete_binary_2, hp_e_bias) @@ -77,7 +99,13 @@ def genTestVectors3_4( ) # Test 2 -def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): +def tests_conversion_3_4( + lp: str, + hp: str, + rounding_mode: str, + test_f: str, + cover_f: str + ) -> None: hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] @@ -104,7 +132,7 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): remaining_rand_bits_2 = f"{random.randint(0, max_remaining_rand):0{remaining_rand_len}b}" full_mantissa_2 = bits + remaining_rand_bits_2 - genTestVectors3_4( + genPNTestVectors( lp, hp, rounding_mode, @@ -130,7 +158,7 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): complete_binary_1 = rs + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" complete_binary_2 = rs + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" - genTestVectors3_4( + genPNTestVectors( lp, hp, rounding_mode, @@ -151,11 +179,10 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): max_mantissa = int("1" * remaining_mantissa_bits, 2) - complete_binary_1 = ( - s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" - ) # randomize the rest of the number complete_binary_2 = s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" + complete_binary_1 = (s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}") + complete_binary_2 = s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" - genTestVectors3_4( + genPNTestVectors( lp, hp, rounding_mode, @@ -173,17 +200,21 @@ def tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f): remaining_mantissa_bits = hp_m_bits max_mantissa = int("1" * hp_m_bits, 2) - complete_binary_1 = ( - f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" # randomize the rest of the number - ) + complete_binary_1 = f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" complete_binary_2 = f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" - genTestVectors3_4( + genPNTestVectors( lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f ) -def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): +def tests_conversion_5_6( + lp: str, + hp: str, + rounding_mode: str, + test_f: str, + cover_f: str + ) -> None: hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] @@ -203,40 +234,46 @@ def tests_conversion_5_6(lp, hp, rounding_mode, test_f, cover_f): hp_m_2 = "1" * (lp_m_bits - 1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_sn_exp - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) # MinNorm - 2 i_ulp: hp_m = "1" * (lp_m_bits - 1) + "1" + "1" + "0" * rem_bits hp_exp = lp_sn_exp - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) # MinNorm - 3 i_ulp: hp_m = "1" * (lp_m_bits - 1) + "1" + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_sn_exp - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) # MinNorm + 1 i_ulp: hp_m = "0" * (lp_m_bits - 1) + "0" + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_n_exp - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) # MinNorm + 2 i_ulp: hp_m = "0" * (lp_m_bits - 1) + "0" + "1" + "0" * rem_bits hp_exp = lp_n_exp - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) # MinNorm + 3 i_ulp: hp_m = "0" * (lp_m_bits - 1) + "0" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_n_exp - genTestVectors3_4(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) -def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): +def tests_conversion_7_8( + lp: str, + hp: str, + rounding_mode: str, + test_f: str, + cover_f: TextIO + ) -> None: hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] @@ -258,7 +295,7 @@ def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): complete_binary_1 = leading_zeros + remaining_binary_1 complete_binary_2 = leading_zeros + remaining_binary_2 - genTestVectors3_4( + genPNTestVectors( lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f ) else: @@ -270,7 +307,7 @@ def tests_conversion_7_8(lp, hp, rounding_mode, test_f, cover_f): complete_binary = f"{random.randint(0, max_remaining_mantissa):0{hp_m_bits}b}" - genTestVectors3_4( + genPNTestVectors( lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary, complete_binary, hp_e_bias, test_f, cover_f ) From 978713b296a4177aa97dc680cced68c8ceb46b7f Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Thu, 26 Mar 2026 16:10:21 -0700 Subject: [PATCH 14/21] updated B5 tests --- src/cover_float/testgen/B5.py | 1094 +++++++++++++++++++++++++++------ 1 file changed, 894 insertions(+), 200 deletions(-) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 9ab6fdb..3ac24f5 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -3,77 +3,140 @@ import random +from pathlib import Path +from random import seed from typing import TextIO +# from cover_float.testgen import softfloat_parser from cover_float.common.constants import ( EXPONENT_BIAS, EXPONENT_BITS, + FLOAT_FMTS, FMT_BF16, FMT_DOUBLE, FMT_HALF, FMT_QUAD, FMT_SINGLE, MANTISSA_BITS, + OP_ADD, OP_CFF, - ROUNDING_MODES, + OP_DIV, + OP_FMADD, + OP_FMSUB, + OP_FNMADD, + OP_FNMSUB, + OP_MUL, + OP_SUB, UNBIASED_EXP, ) +from cover_float.common.util import reproducible_hash from cover_float.reference import run_and_store_test_vector B5_FMTS = [FMT_QUAD, FMT_DOUBLE, FMT_SINGLE, FMT_BF16, FMT_HALF] +ROUNDING_MODES = ["00", "01", "02", "03", "04", "05"] +FMA_OPS = [OP_FMADD, OP_FMSUB, OP_FNMADD, OP_FNMSUB] + + +# def generate_FP( +# input_e_bitwidth: int, input_sign: str, input_exponent: int, input_mantissa: str, input_bias: int +# ) -> str: +# exponent = f"{input_exponent + input_bias:0{input_e_bitwidth}b}" +# complete = input_sign + exponent + input_mantissa +# fp_complete = format(int(complete, 2), "X") + + +# return fp_complete def generate_FP( - input_e_bitwidth: int, - input_sign: str, - input_exponent: int, - input_mantissa: str, - input_bias: int - ) -> str: - exponent = f"{input_exponent + input_bias:0{input_e_bitwidth}b}" + input_e_bitwidth: int, input_sign: str, input_exponent: int, input_mantissa: str, input_bias: int +) -> str: + # 1. Calculate and format the exponent + exp_val = input_exponent + input_bias + exponent = f"{exp_val:0{input_e_bitwidth}b}" + + # 2. Check for Exponent Overflow/Underflow + if len(exponent) != input_e_bitwidth: + raise ValueError( + f"Alignment Error: Exponent binary '{exponent}' is {len(exponent)} bits long. " + f"Expected exactly {input_e_bitwidth} bits. (Calculated value was {exp_val})" + ) + + # 3. Validate Sign Bit + if len(input_sign) != 1 or input_sign not in ("0", "1"): + raise ValueError(f"Alignment Error: Sign bit must be exactly '0' or '1'. Got: '{input_sign}'") + + # 4. Construct the full binary string complete = input_sign + exponent + input_mantissa - fp_complete = format(int(complete, 2), "X") + total_bits = len(complete) + + # 5. Validate total bit length is a clean multiple of 4 (for hex conversion) + if total_bits % 4 != 0: + raise ValueError( + f"Alignment Error: Total bit length ({total_bits}) is not a multiple of 4. " + f"Sign: 1, Exp: {input_e_bitwidth}, Mantissa: {len(input_mantissa)}" + ) + + # 6. Convert to Hex AND explicitly pad to the correct number of characters + hex_chars_needed = total_bits // 4 + fp_complete = format(int(complete, 2), "X").zfill(hex_chars_needed) return fp_complete -def tests_conversion_1_2( - lp: str, - hp: str, - rounding_mode: str, - test_f: TextIO, - cover_f: TextIO - ) -> None: +def convert_grs( + hp: str, lp: str, g_exp: int, grs: str, sign: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO +) -> None: hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Make the exponent for subnorm values - lp_m_bits = MANTISSA_BITS[lp] - - hp_max_exp = lp_sn_exp - 1 - hp_min_exp = lp_sn_exp - lp_m_bits - - if hp == FMT_SINGLE and lp == FMT_BF16: # Different case for FP_32 and BF_16 - hp_min_exp = lp_sn_exp - hp_max_exp = lp_sn_exp - - input_1_exponent = random.randint(hp_min_exp, hp_max_exp) - input_2_exponent = random.randint(hp_min_exp, hp_max_exp) + hp_min_exp = UNBIASED_EXP[hp][0] + hp_minsn_exp = hp_min_exp - hp_m_bits + + # Determine the actual exponent, based on desired grs pattern + grs_int = int(grs, 2) + first_1 = grs.index("1") + + input_exp = g_exp - first_1 if grs_int != 1 else random.randint(hp_minsn_exp, g_exp - 2) + + # Generate the mantissa + input_mant = 0 + bits_left = hp_m_bits - max(hp_min_exp - input_exp, 0) + sn = bits_left < hp_m_bits + # Handle the first bit + + # Like for BF_16 to Single, you're going from sn -> sn + if sn: + input_mant += 1 << bits_left + bits_left -= 1 + if int(grs, 2) != 1: + grs = grs.replace("1", "0", 1) + if grs[1] == "0": + bits_left -= 1 + elif grs[1] == "1": + input_mant += 1 << bits_left + + if grs[2] == "1": + input_mant += random.randint(1, (1 << bits_left) - 1) + + # Normalize exponent + input_exp = max(input_exp, hp_min_exp - 1) + # Make sure exponent has correct padding + input_mant_bin = f"{input_mant:0{hp_m_bits}b}" + + input_fp = generate_FP(hp_e_bits, sign, input_exp, input_mant_bin, hp_e_bias) + run_and_store_test_vector( + f"{OP_CFF}_{rounding_mode}_{input_fp}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f + ) - max_mantissa = int("1" * hp_m_bits, 2) - input_1_mantissa = f"{random.randint(0, max_mantissa):0{hp_m_bits}b}" - input_2_mantissa = f"{random.randint(0, max_mantissa):0{hp_m_bits}b}" +def tests_conversion_1_2(lp: str, hp: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + hashval = reproducible_hash(OP_CFF + lp + "b5") + seed(hashval) + lp_min_exp = UNBIASED_EXP[lp][0] - input_value_1 = generate_FP(hp_e_bits, "0", input_1_exponent, input_1_mantissa, hp_e_bias) - input_value_2 = generate_FP(hp_e_bits, "1", input_2_exponent, input_2_mantissa, hp_e_bias) - - run_and_store_test_vector( - f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f - ) # Test 1 - run_and_store_test_vector( - f"{OP_CFF}_{rounding_mode}_{input_value_2}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f - ) # Test 2 + convert_grs(hp, lp, lp_min_exp + 1, "001", "0", rounding_mode, test_f, cover_f) + convert_grs(hp, lp, lp_min_exp + 1, "001", "1", rounding_mode, test_f, cover_f) def genPNTestVectors( @@ -85,9 +148,12 @@ def genPNTestVectors( complete_binary_1: str, complete_binary_2: str, hp_e_bias: int, - test_f: str, - cover_f: str + test_f: TextIO, + cover_f: TextIO, ) -> None: + + hashval = reproducible_hash(OP_CFF + lp + "b5") + seed(hashval) input_value_1 = generate_FP(hp_e_bits, "0", hp_exp, complete_binary_1, hp_e_bias) input_value_2 = generate_FP(hp_e_bits, "1", hp_exp, complete_binary_2, hp_e_bias) @@ -99,127 +165,30 @@ def genPNTestVectors( ) # Test 2 -def tests_conversion_3_4( - lp: str, - hp: str, - rounding_mode: str, - test_f: str, - cover_f: str - ) -> None: - hp_m_bits = MANTISSA_BITS[hp] - hp_e_bits = EXPONENT_BITS[hp] - hp_e_bias = EXPONENT_BIAS[hp] +def tests_conversion_3_4(lp: str, hp: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + + hashval = reproducible_hash(OP_CFF + lp + "b5") + seed(hashval) lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] - hp_sn_lp_exp = lp_sn_exp - lp_m_bits - - if hp == FMT_SINGLE and lp == FMT_BF16: - # We can't perform the same exponent operations when hp = lp in single -> bf 16 - # the desired values of the gaurd, round, and sticky bits: - # minSN -3 i_ulp, minSN - 2 i_ulp, minSN - 1 i_ulp, minSN, minSN + 1 i_ulp, minSN + 2 i_ulp, minSN + 3 i_ulp - grs = ["001", "010", "011", "100", "101", "110", "111"] - - leading_zeros_len = lp_m_bits - 1 - determined_len = leading_zeros_len + 3 - remaining_rand_len = hp_m_bits - determined_len - max_remaining_rand = int("1" * remaining_rand_len, 2) - - for bits in grs: - remaining_rand_bits_1 = f"{random.randint(0, max_remaining_rand):0{remaining_rand_len}b}" - full_mantissa_1 = bits + remaining_rand_bits_1 - - remaining_rand_bits_2 = f"{random.randint(0, max_remaining_rand):0{remaining_rand_len}b}" - full_mantissa_2 = bits + remaining_rand_bits_2 - - genPNTestVectors( - lp, - hp, - rounding_mode, - hp_e_bits, - lp_sn_exp, - full_mantissa_1, - full_mantissa_2, - hp_e_bias, - test_f, - cover_f, - ) - else: - # MinSN, MinSN + 1 ulp, MinSN + 2 ulp, MinSN + 3 ulp - for i in range(0, int("11", 2) + 1): # Iterate over differt round and sticky bits - hp_exp = hp_sn_lp_exp - rs = f"{i:02b}" # round and sticky bit - - remaining_mantissa_bits = ( - hp_m_bits - 2 - ) # find out what part of the mantissa bits left need to be randomized - max_mantissa = int("1" * remaining_mantissa_bits, 2) - - complete_binary_1 = rs + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" - complete_binary_2 = rs + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" - - genPNTestVectors( - lp, - hp, - rounding_mode, - hp_e_bits, - hp_exp, - complete_binary_1, - complete_binary_2, - hp_e_bias, - test_f, - cover_f, - ) - - # MinSN - 1 ulp, MinSN - 2 ulp - for i in range(0, int("1", 2) + 1): - hp_exp = hp_sn_lp_exp - 1 - s = f"{i:01b}" # sticky bit - remaining_mantissa_bits = hp_m_bits - 1 - - max_mantissa = int("1" * remaining_mantissa_bits, 2) - - complete_binary_1 = (s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}") - complete_binary_2 = s + f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" - - genPNTestVectors( - lp, - hp, - rounding_mode, - hp_e_bits, - hp_exp, - complete_binary_1, - complete_binary_2, - hp_e_bias, - test_f, - cover_f, - ) - - # #MinSN - 3 ulp - hp_exp = hp_sn_lp_exp - 2 - remaining_mantissa_bits = hp_m_bits - max_mantissa = int("1" * hp_m_bits, 2) + lp_min_exp = lp_sn_exp - lp_m_bits - complete_binary_1 = f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" - complete_binary_2 = f"{random.randint(0, max_mantissa):0{remaining_mantissa_bits}b}" + grs = ["001", "010", "011", "100", "101", "110", "111"] + for bits in grs: + convert_grs(hp, lp, lp_min_exp + 1, bits, "0", rounding_mode, test_f, cover_f) + convert_grs(hp, lp, lp_min_exp + 1, bits, "1", rounding_mode, test_f, cover_f) - genPNTestVectors( - lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f - ) +def tests_conversion_5_6(lp: str, hp: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: -def tests_conversion_5_6( - lp: str, - hp: str, - rounding_mode: str, - test_f: str, - cover_f: str - ) -> None: + hashval = reproducible_hash(OP_CFF + lp + "b5") + seed(hashval) hp_m_bits = MANTISSA_BITS[hp] + lp_n_exp = UNBIASED_EXP[lp][0] + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] - lp_n_exp = UNBIASED_EXP[lp][0] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms lp_m_bits = MANTISSA_BITS[lp] if ( @@ -267,57 +236,23 @@ def tests_conversion_5_6( genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) -def tests_conversion_7_8( - lp: str, - hp: str, - rounding_mode: str, - test_f: str, - cover_f: TextIO - ) -> None: - hp_m_bits = MANTISSA_BITS[hp] - hp_e_bits = EXPONENT_BITS[hp] - hp_e_bias = EXPONENT_BIAS[hp] - lp_sn_exp = UNBIASED_EXP[lp][0] - 1 - hp_sn_exp = UNBIASED_EXP[hp][0] - 1 - lp_m_bits = MANTISSA_BITS[lp] - - # Different for fp_16 and bf_16 because they have the same exponent - if hp == FMT_SINGLE and lp == FMT_BF16: - hp_exp = hp_sn_exp - leading_zero_bits = lp_m_bits - leading_zeros = "0" * lp_m_bits - remaining_bits = hp_m_bits - lp_m_bits - max_remaining_mantissa = int("1" * remaining_bits, 2) - - remaining_binary_1 = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" - remaining_binary_2 = f"{random.randint(max_remaining_mantissa, max_remaining_mantissa):0{remaining_bits}b}" - - complete_binary_1 = leading_zeros + remaining_binary_1 - complete_binary_2 = leading_zeros + remaining_binary_2 - - genPNTestVectors( - lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary_1, complete_binary_2, hp_e_bias, test_f, cover_f - ) - else: - hp_max_exp = lp_sn_exp - lp_m_bits - 1 # put the hidden 1 of the hp in the rounding bit of the lp - hp_min_exp = hp_sn_exp - max_remaining_mantissa = int("1" * hp_m_bits, 2) - - hp_exp = random.randint(hp_min_exp, hp_max_exp) +def tests_conversion_7_8(lp: str, hp: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + hashval = reproducible_hash(OP_CFF + lp + "b5") + seed(hashval) - complete_binary = f"{random.randint(0, max_remaining_mantissa):0{hp_m_bits}b}" + lp_sn_exp = UNBIASED_EXP[lp][0] - 1 - genPNTestVectors( - lp, hp, rounding_mode, hp_e_bits, hp_exp, complete_binary, complete_binary, hp_e_bias, test_f, cover_f - ) + convert_grs(hp, lp, lp_sn_exp + 2, "001", "0", rounding_mode, test_f, cover_f) + convert_grs(hp, lp, lp_sn_exp + 2, "001", "0", rounding_mode, test_f, cover_f) -def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): +def tests_conversion_9(lp: str, hp: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + hashval = reproducible_hash(OP_CFF + lp + "b5") + seed(hashval) hp_m_bits = MANTISSA_BITS[hp] hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] lp_sn_exp = UNBIASED_EXP[lp][0] - 1 # Account for subnorms - lp_m_bits = MANTISSA_BITS[lp] max_m_value = int("1" * hp_m_bits, 2) hp_exp = lp_sn_exp @@ -329,15 +264,17 @@ def tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f): run_and_store_test_vector( f"{OP_CFF}_{rounding_mode}_{input_value_1}_{32 * '0'}_{32 * '0'}_{hp}_{32 * '0'}_{lp}_00", test_f, cover_f ) # Test 1 - hp_exp += 1 + hp_exp = lp_sn_exp + i + 1 -def convertTests(test_f, cover_f): +def convertTests(test_f: TextIO, cover_f: TextIO) -> None: # All conversion tests: for i_hp in range(len(B5_FMTS)): hp = B5_FMTS[i_hp] for i_lp in range(i_hp + 1, len(B5_FMTS)): lp = B5_FMTS[i_lp] + # hp = FMT_SINGLE + # lp = FMT_HALF for rounding_mode in ROUNDING_MODES: tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f) tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f) @@ -346,9 +283,766 @@ def convertTests(test_f, cover_f): tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f) -def main(): - with open("./tests/testvectors/B5_tv.txt", "w") as test_f, open("./tests/covervectors/B5_cv.txt", "w") as cover_f: +def genSpecExp(precision: str, target: int, hashString: str) -> tuple[int, int]: + hashval = reproducible_hash(hashString) + seed(hashval) + m_bits = MANTISSA_BITS[precision] + min_exp = UNBIASED_EXP[precision][0] + + min_sn = min_exp - m_bits + + a_exp = random.randint(min_sn, target - min_sn) + b_exp = target - a_exp + return (a_exp, b_exp) + + +def genSpecExp_div(precision: str, target: int, hashString: str) -> tuple[int, int]: + hashval = reproducible_hash(hashString) + seed(hashval) + m_bits = MANTISSA_BITS[precision] + min_exp = UNBIASED_EXP[precision][0] + max_exp = UNBIASED_EXP[precision][1] + + # Absolute lowest effective exponent (subnormal floor) + min_sn = min_exp - m_bits + + # Mathematical rule for Division: target = a_exp - b_exp + # Therefore: b_exp = a_exp - target + + lower_bound = max(min_sn, min_sn + target) + upper_bound = min(max_exp, max_exp + target) + + if lower_bound > upper_bound: + lower_bound = upper_bound + + a_exp = random.randint(lower_bound, upper_bound) + b_exp = a_exp - target + + return (a_exp, b_exp) + + +def genRandExp(precision: str, max_exp: int, hashString: str) -> tuple[int, int]: + hashval = reproducible_hash(hashString) + seed(hashval) + m_bits = MANTISSA_BITS[precision] + min_exp = UNBIASED_EXP[precision][0] + + smallest_res_exp = min_exp - (2 * m_bits) + target = random.randint(smallest_res_exp, max_exp) + return genSpecExp(precision, target, hashString) + + +def get_grs_mant(precision: str, a_exp: int, b_exp: int, hashString: str, grs: str) -> tuple[str, str]: + m_bits = MANTISSA_BITS[precision] + e_min = UNBIASED_EXP[precision][0] + min_sn = e_min - m_bits + + # Since we're unbiased, if a_exp > e_min, result < 0, meaning it's normal and bits_left = m_bits + a_bits_left = m_bits - max(e_min - a_exp, 0) + b_bits_left = m_bits - max(e_min - b_exp, 0) + + grs_int = int(grs, 2) + + # Loop until we get desired results: + met_conditions = False + + cycles_attempted = 0 + + a_mantissa = 0 + b_mantissa = 0 + + # Set up a_mantissa and b_mantissa, they are different based on the grs pattern: + a_rBit = False + b_rBit = False + if grs_int == 6: + if a_exp == min_sn or b_exp == min_sn: # If exp = min_sn, then you can't have the rBit + if a_exp == min_sn and b_exp == min_sn: + raise ValueError("a_exp and b_exp can't both be min_sn") + elif a_exp == min_sn: + b_rBit = True + elif b_exp == min_sn: + a_rBit = True + else: # Random selection otherwise + a_rBit = random.randint(0, 1) == 1 # If 1 is randomly selected, then a_rBit = True + b_rBit = not a_rBit + if a_rBit: + a_mantissa += 1 << a_bits_left - 1 + elif b_rBit: + b_mantissa += 1 << b_bits_left - 1 + + # grs 2 and 4 want need 0s after specified 1 bit + if grs_int != 2 and grs_int != 6 and grs_int != 4: + while not met_conditions: + seed(cycles_attempted) # Make deterministic + + # Scales down values for test 5 to get a r_bit = 0 + mantissa_scalar = 1 + if grs_int == 5: + mantissa_scalar = (cycles_attempted % 3) + 1 + + a_mantissa = random.randint(0, (1 << a_bits_left) - 1) // mantissa_scalar + b_mantissa = random.randint(0, (1 << b_bits_left) - 1) // mantissa_scalar + + # Add hidden 1 + a_mantissa += 1 << a_bits_left + b_mantissa += 1 << b_bits_left + + product_a_b = a_mantissa * b_mantissa + + # To avoid normalization, the product must be < 2 + product_bits = a_bits_left + b_bits_left + 2 + decimal_bit = a_bits_left + b_bits_left # The first bit where there will be a decimal + maxNorm = 1 << (product_bits - 1) # Really the smallest nonNorm + + if product_a_b < maxNorm: + if grs_int == 5 or grs_int == 8: + subtract_g_bit = product_a_b - (1 << decimal_bit) + subtract_r_bit = subtract_g_bit - (1 << (decimal_bit - 1)) + if grs_int == 5: + met_conditions = subtract_r_bit < 0 + elif grs_int == 8: + met_conditions = subtract_r_bit > 0 + else: + met_conditions = True + + if met_conditions: + # Once a_mantissa and b_mantissa are generated, I can remove the hidden 1 if normal + a_mantissa -= 1 << a_bits_left + b_mantissa -= 1 << b_bits_left + + cycles_attempted += 1 + + # Add leading 1 if SN + if a_bits_left < m_bits: + a_mantissa += 1 << a_bits_left + if b_bits_left < m_bits: + b_mantissa += 1 << b_bits_left + + bin_a_mantissa = f"{a_mantissa:0{m_bits}b}" + bin_b_mantissa = f"{b_mantissa:0{m_bits}b}" + + return (bin_a_mantissa, bin_b_mantissa) + + +def mul_grs_gen( + precision: str, + rounding_mode: str, + grs: str, + g_exp: int, + sign: str, + test_f: TextIO, + cover_f: TextIO, + hashEnding: str, + genTests: bool, +) -> tuple[str, str]: + hashString = "b5" + OP_MUL + precision + rounding_mode + hashEnding + seed(hashString) + + # g_exp is needed for going below normal, getting specific subnorm results + e_bits = EXPONENT_BITS[precision] + e_bias = EXPONENT_BIAS[precision] + min_exp = UNBIASED_EXP[precision][0] + sn_exp = min_exp - 1 + + first_bit = g_exp - grs.index("1") + + if sign == "0": + a_sign = random.randint(0, 1) + b_sign = a_sign + else: + a_sign = random.randint(0, 1) + b_sign = (a_sign + 1) % 2 + + grs_int = int(grs, 2) + if grs_int == 1: + a_exp, b_exp = genRandExp(precision, first_bit, hashString + grs + sign) + else: + a_exp, b_exp = genSpecExp(precision, first_bit, hashString + grs + sign) + + a_mant, b_mant = get_grs_mant(precision, a_exp, b_exp, hashString + grs + sign, grs) + + # Normalize exponents + a_exp = max(a_exp, sn_exp) + b_exp = max(b_exp, sn_exp) + + a = generate_FP(e_bits, str(a_sign), a_exp, a_mant, e_bias) + b = generate_FP(e_bits, str(b_sign), b_exp, b_mant, e_bias) + + if genTests: + run_and_store_test_vector( + f"{OP_MUL}_{rounding_mode}_{a}_{b}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f + ) + + return (a, b) + + +def tests_multiply_1_2(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + hashString = "b5" + OP_MUL + precision + rounding_mode + seed(hashString) + + min_exp = UNBIASED_EXP[precision][0] + sn_exp = min_exp - 1 + + mul_grs_gen(precision, rounding_mode, "001", sn_exp, "0", test_f, cover_f, "1/2", True) # Positive Test + mul_grs_gen(precision, rounding_mode, "001", sn_exp, "1", test_f, cover_f, "1/2", True) # Negative Test + + +def tests_multiply_3_4(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + hashString = "b5" + OP_MUL + precision + rounding_mode + seed(hashString) + + m_bits = MANTISSA_BITS[precision] + min_exp = UNBIASED_EXP[precision][0] + + minSNPos = min_exp - m_bits # Treating the minSN as normalized + + # minSN - 3 ulp G = 0, R = 0, S = 1 + mul_grs_gen(precision, rounding_mode, "001", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True) # Positive Test + mul_grs_gen(precision, rounding_mode, "001", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True) # Negative Test + + # minSN - 2 ulp G = 0, R = 1, S = 0 + mul_grs_gen(precision, rounding_mode, "010", minSNPos, "0", test_f, cover_f, "minSN-2ulppos", True) # Positive Test + mul_grs_gen(precision, rounding_mode, "010", minSNPos, "1", test_f, cover_f, "minSN-2ulpneg", True) # Negative Test + + # minSN - 1 ulp G = 0, R = 1, S = 1 + mul_grs_gen(precision, rounding_mode, "011", minSNPos, "0", test_f, cover_f, "minSN-1ulppos", True) # Positive Test + mul_grs_gen(precision, rounding_mode, "011", minSNPos, "1", test_f, cover_f, "minSN-1ulpneg", True) # Negative Test + + # minSN G = 1, R = 0, S = 0 + mul_grs_gen(precision, rounding_mode, "100", minSNPos, "0", test_f, cover_f, "minSNpos", True) # Positive Test + mul_grs_gen(precision, rounding_mode, "100", minSNPos, "1", test_f, cover_f, "minSNneg", True) # Negative Test + + # minSN + 1 ulp G = 1, R = 0, S = 1 + mul_grs_gen(precision, rounding_mode, "101", minSNPos, "0", test_f, cover_f, "minSN+1ulppos", True) # Positive Test + mul_grs_gen(precision, rounding_mode, "101", minSNPos, "1", test_f, cover_f, "minSN+1ulpneg", True) # Negative Test + + # minSN + 2 ulp G = 1, R = 1, S = 0 + mul_grs_gen(precision, rounding_mode, "110", minSNPos, "0", test_f, cover_f, "minSN+2ulppos", True) # Positive Test + mul_grs_gen(precision, rounding_mode, "110", minSNPos, "1", test_f, cover_f, "minSN+2ulpneg", True) # Negative Test + + # minSN + 3 ulp G = 1, R = 1, S = 1 + mul_grs_gen(precision, rounding_mode, "111", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True) # Positive Test + mul_grs_gen(precision, rounding_mode, "111", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True) # Negative Test + + +def tests_multiply_5_6(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + min_exp = UNBIASED_EXP[precision][0] + # MinNorm - 3ulp + mul_grs_gen(precision, rounding_mode, "100", min_exp, "0", test_f, cover_f, "minExp", True) # Positive Test + mul_grs_gen(precision, rounding_mode, "100", min_exp, "1", test_f, cover_f, "minExp", True) # Negative Test + + # MinNorm + mul_grs_gen(precision, rounding_mode, "100", min_exp, "0", test_f, cover_f, "minExp", True) # Positive Test + mul_grs_gen(precision, rounding_mode, "100", min_exp, "1", test_f, cover_f, "minExp", True) # Negative Test + + +def tests_multiply_7_8(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + m_bits = MANTISSA_BITS[precision] + min_exp = UNBIASED_EXP[precision][0] + + target_exp = min_exp - m_bits + + mul_grs_gen(precision, rounding_mode, "011", target_exp, "0", test_f, cover_f, "1/2 pos", True) + mul_grs_gen(precision, rounding_mode, "011", target_exp, "1", test_f, cover_f, "1/2 neg", True) + + +def tests_multiply_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + min_exp = UNBIASED_EXP[precision][0] + + # all values from minNorm.exp to minNorm.exp + 5 + min_exp_range = min_exp - 1 # SN exp, or minNorm.exp + max_exp_range = min_exp_range + 5 + + for target_exp in range(min_exp_range, max_exp_range + 1): # Because end is exclusive + seed("b5" + OP_MUL + precision + rounding_mode + str(target_exp)) + sign = str(random.randint(0, 1)) + + mul_grs_gen(precision, rounding_mode, "011", target_exp, sign, test_f, cover_f, str(target_exp), True) + + +def multiplyTests(test_f: TextIO, cover_f: TextIO) -> None: + for precision in FLOAT_FMTS: + for rounding_mode in ROUNDING_MODES: + tests_multiply_1_2(precision, rounding_mode, test_f, cover_f) + tests_multiply_3_4(precision, rounding_mode, test_f, cover_f) + tests_multiply_5_6(precision, rounding_mode, test_f, cover_f) + tests_multiply_7_8(precision, rounding_mode, test_f, cover_f) + tests_multiply_9(precision, rounding_mode, test_f, cover_f) + + +def fma_gen( + operation: str, + precision: str, + rounding_mode: str, + product_sign: int, + product_grs: str, + product_exponent: int, + addend_sign: int, + addend_pattern: str, + test_f: TextIO, + cover_f: TextIO, + hashEnding: str, +) -> None: + m_bits = MANTISSA_BITS[precision] + e_bits = EXPONENT_BITS[precision] + e_bias = EXPONENT_BIAS[precision] + min_exp = UNBIASED_EXP[precision][0] + + fma_op_key = { + OP_FMADD: {"mul_sign": 0, "add_sign": 0}, + OP_FMSUB: {"mul_sign": 0, "add_sign": 1}, + OP_FNMADD: {"mul_sign": 1, "add_sign": 0}, + OP_FNMSUB: {"mul_sign": 1, "add_sign": 1}, + } + + # Determine the desired output signs for multiplication and addition + op_mul_sign = fma_op_key[operation]["mul_sign"] + op_add_sign = fma_op_key[operation]["add_sign"] + + mul_sign = (op_mul_sign + product_sign) % 2 + c_sign = (op_add_sign + addend_sign) % 2 + + # Generate the multiplication testvectors + a, b = mul_grs_gen( + precision, rounding_mode, product_grs, product_exponent, str(mul_sign), test_f, cover_f, hashEnding, False + ) + + # Generate the addition testvector + c_exp = -1 + c_mant = -1 + if addend_pattern == "1*m-1_0": + c_exp = min_exp - 1 + c_mant = (1 << m_bits) - 2 # all ones throughout the mantissa + elif addend_pattern == "min_n": + c_exp = min_exp + c_mant = 0 + elif addend_pattern == "2*min_sn": + c_exp = min_exp - 1 + c_mant = 2 + elif addend_pattern == "min_sn": + c_exp = min_exp - 1 + c_mant = 1 + elif addend_pattern == "1_0*m-1_1": + c_exp = min_exp + c_mant = 1 + elif addend_pattern == "rand_sn": + c_exp = min_exp - random.randint(1, m_bits) # Upper: Just SN, Lower: Leave 1 spot open on the end + max_mant = (1 << (min_exp - c_exp)) - 1 + c_mant = random.randint(1, max_mant) + c_mant = max_mant + # Normalize c_exp + c_exp = max(min_exp - 1, c_exp) + elif addend_pattern == "rand_n": + c_exp = min_exp + max_mant = 1 << m_bits + c_mant = random.randint(1, max_mant) + elif addend_pattern[0 : addend_pattern.index("_")] == "randexp": + exp_string = addend_pattern[addend_pattern.index("_") + 1 : len(addend_pattern)] + c_exp = 0 - int(exp_string) + max_mant = (1 << m_bits) - 1 + c_mant = random.randint(1, max_mant) + + c_bin_mant = f"{c_mant:0{m_bits}b}" + c = generate_FP(e_bits, str(c_sign), c_exp, c_bin_mant, e_bias) + run_and_store_test_vector( + f"{operation}_{rounding_mode}_{a}_{b}_{c}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f + ) + + +def tests_fma_1_2(operation: str, precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + m_bits = MANTISSA_BITS[precision] + min_exp = UNBIASED_EXP[precision][0] + + minSNPos = min_exp - m_bits + + fma_gen( + operation, precision, rounding_mode, 0, "001", minSNPos, 1, "rand_sn", test_f, cover_f, "pos_1" + ) # adds decreased magnitude, can't be normal + fma_gen( + operation, precision, rounding_mode, 1, "001", minSNPos, 0, "rand_sn", test_f, cover_f, "neg_2" + ) # add decreases magnitude, can't be normal + + +def tests_fma_3_4(operation: str, precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + m_bits = MANTISSA_BITS[precision] + min_exp = UNBIASED_EXP[precision][0] + + minSNPos = min_exp - m_bits + + # MinSN - 3 ulp; G = 0, R = 0, S = 1 + fma_gen( + operation, precision, rounding_mode, 0, "101", minSNPos, 1, "min_sn", test_f, cover_f, "pos_-3_ulp_3" + ) # Get rid of G Bit and some more from sticky, result is negative + fma_gen(operation, precision, rounding_mode, 1, "101", minSNPos, 0, "min_sn", test_f, cover_f, "neg_-3_ulp_4") + + # MinSN - 2 ulp; G = 0, R = 1, S = 0 + fma_gen( + operation, precision, rounding_mode, 1, "010", minSNPos, 0, "min_sn", test_f, cover_f, "pos_-2_ulp_3" + ) # Subtract minSN from 1 bit 2*minSN + fma_gen(operation, precision, rounding_mode, 0, "010", minSNPos, 1, "min_sn", test_f, cover_f, "neg_-2_ulp_4") + + # MinSN - 1 ulp; G = 0, R = 1, S = 1 + fma_gen(operation, precision, rounding_mode, 0, "111", minSNPos, 1, "min_sn", test_f, cover_f, "pos_-1_ulp_3") + fma_gen(operation, precision, rounding_mode, 1, "111", minSNPos, 0, "min_sn", test_f, cover_f, "neg_-1_ulp_4") + + # #MinSN; G = 1, R = 0, S = 0 + fma_gen(operation, precision, rounding_mode, 0, "100", minSNPos, 1, "2*min_sn", test_f, cover_f, "pos_minSN_3") + fma_gen(operation, precision, rounding_mode, 1, "100", minSNPos, 0, "2*min_sn", test_f, cover_f, "neg_minSN_4") + + # MinSN + 1 ulp; G = 1, R = 0, S = 1 + fma_gen(operation, precision, rounding_mode, 0, "001", minSNPos, 0, "min_sn", test_f, cover_f, "pos_+1_ulp_3") + fma_gen(operation, precision, rounding_mode, 1, "001", minSNPos, 1, "min_sn", test_f, cover_f, "neg_+1_ulp_4") + + # MinSN + 2 ulp; G = 1, R = 1, S = 0 + fma_gen(operation, precision, rounding_mode, 0, "010", minSNPos, 0, "min_sn", test_f, cover_f, "pos_+2_ulp_3") + fma_gen(operation, precision, rounding_mode, 1, "010", minSNPos, 1, "min_sn", test_f, cover_f, "neg_+2_ulp_4") + + # MinSN + 3 ulp; G = 1, R = 1, S = 1 + fma_gen(operation, precision, rounding_mode, 0, "011", minSNPos, 0, "min_sn", test_f, cover_f, "pos_+3_ulp_3") + fma_gen(operation, precision, rounding_mode, 1, "011", minSNPos, 1, "min_sn", test_f, cover_f, "neg_+3_ulp_4") + + +def tests_fma_5_6(operation: str, precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + m_bits = MANTISSA_BITS[precision] + min_exp = UNBIASED_EXP[precision][0] + + minSNPos = min_exp - m_bits + + # MinN - 3 ulp; G = 1, R = 0, S = 0 + fma_gen( + operation, precision, rounding_mode, 0, "100", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-3_ulp_5" + ) # Get rid of G Bit and some more from sticky, result is negative + fma_gen(operation, precision, rounding_mode, 1, "100", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-3_ulp_6") + + # MinN - 2 ulp; G = 1, R = 0, S = 1 + fma_gen( + operation, precision, rounding_mode, 0, "101", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-2_ulp_5" + ) # Get rid of G Bit and some more from sticky, result is negative + fma_gen(operation, precision, rounding_mode, 1, "101", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-2_ulp_6") + + # MinN - 1 ulp; G = 1, R = 1, S = 1 + fma_gen( + operation, precision, rounding_mode, 0, "110", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-2_ulp_5" + ) # Get rid of G Bit and some more from sticky, result is negative + fma_gen(operation, precision, rounding_mode, 1, "110", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-2_ulp_6") + + # MinN + fma_gen(operation, precision, rounding_mode, 1, "100", minSNPos, 0, "1_0*m-1_1", test_f, cover_f, "pos_norm_5") + fma_gen(operation, precision, rounding_mode, 0, "100", minSNPos, 1, "1_0*m-1_1", test_f, cover_f, "neg_norm_6") + + # MinN + 1 + fma_gen(operation, precision, rounding_mode, 0, "001", minSNPos, 0, "min_n", test_f, cover_f, "pos_+1_ulp_5") + fma_gen(operation, precision, rounding_mode, 1, "001", minSNPos, 1, "min_n", test_f, cover_f, "neg_+1_ulp_6") + + # MinN + 2 + fma_gen(operation, precision, rounding_mode, 0, "010", minSNPos, 0, "min_n", test_f, cover_f, "pos_+2_ulp_5") + fma_gen(operation, precision, rounding_mode, 1, "010", minSNPos, 1, "min_n", test_f, cover_f, "neg_+2_ulp_6") + + # MinN + 3 + fma_gen(operation, precision, rounding_mode, 0, "011", minSNPos, 0, "min_n", test_f, cover_f, "pos_+3_ulp_5") + fma_gen(operation, precision, rounding_mode, 1, "011", minSNPos, 1, "min_n", test_f, cover_f, "neg_+3_ulp_6") + + +def tests_fma_7_8(operation: str, precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + m_bits = MANTISSA_BITS[precision] + min_exp = UNBIASED_EXP[precision][0] + + minSNPos = min_exp - m_bits + + fma_gen( + operation, precision, rounding_mode, 0, "011", minSNPos + 1, 1, "min_sn", test_f, cover_f, "pos_-3_ulp_3" + ) # Get rid of G Bit and some more from sticky, result is negative + fma_gen(operation, precision, rounding_mode, 1, "011", minSNPos + 1, 0, "min_sn", test_f, cover_f, "neg_-3_ulp_4") + + +def tests_fma_9(operation: str, precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + m_bits = MANTISSA_BITS[precision] + min_exp = UNBIASED_EXP[precision][0] + + minSNPos = min_exp - m_bits + for exp in range(min_exp - 1, min_exp + 6): + fma_gen( + operation, + precision, + rounding_mode, + 0, + "001", + minSNPos + 2, + 1, + f"randexp_{abs(exp)}", + test_f, + cover_f, + f"pos_-3_ulp_{exp}", + ) + fma_gen( + operation, + precision, + rounding_mode, + 1, + "001", + minSNPos + 2, + 0, + f"randexp_{abs(exp)}", + test_f, + cover_f, + f"pos_-3_ulp_{exp}", + ) + + +def fmaTests(test_f: TextIO, cover_f: TextIO) -> None: + for operation in FMA_OPS: + for precision in FLOAT_FMTS: + # precision = FMT_HALF + # operation = OP_FMADD + for rounding_mode in ROUNDING_MODES: + tests_fma_1_2(operation, precision, rounding_mode, test_f, cover_f) + tests_fma_3_4(operation, precision, rounding_mode, test_f, cover_f) + tests_fma_5_6(operation, precision, rounding_mode, test_f, cover_f) + tests_fma_7_8(operation, precision, rounding_mode, test_f, cover_f) + tests_fma_9(operation, precision, rounding_mode, test_f, cover_f) + + +# def get_div_mant(precision: str, a_exp: int, b_exp: int, hashString: str, grs: str) -> str: + + +def div_grs_gen( + test_f: TextIO, + cover_f: TextIO, + grs: str, + g_bit_pos: int, + precision: str, + rounding_mode: str, + target_exp: int, + sign: str, + hashString: str, +) -> tuple[str, str]: + m_bits = MANTISSA_BITS[precision] + e_bits = EXPONENT_BITS[precision] + e_bias = EXPONENT_BIAS[precision] + min_exp = UNBIASED_EXP[precision][0] + + # Determine exponents, subtract to target_exp + possible_exponents = genSpecExp_div(precision, target_exp, hashString) + + a_exp = max(possible_exponents) + b_exp = min(possible_exponents) + + a_bits_left = m_bits - max(min_exp - a_exp, 0) + b_bits_left = m_bits - max(min_exp - b_exp, 0) + + b_max_mant = (1 << b_bits_left) - 1 + a_max_mant = (1 << a_bits_left) - 1 + + b_mantissa = random.randint(1, b_max_mant - 1) + a_mantissa = random.randint(b_mantissa // 2, a_max_mant) + + if sign == "0": + a_sign = random.randint(0, 1) + b_sign = a_sign + else: + a_sign = random.randint(0, 1) + b_sign = (a_sign + 1) % 2 + + a_exp_norm = max(a_exp, min_exp - 1) + b_exp_norm = max(b_exp, min_exp - 1) + + if a_bits_left < m_bits: + a_mantissa += 1 << a_bits_left + if b_bits_left < m_bits: + b_mantissa += 1 << b_bits_left + + a = f"{a_mantissa:0{m_bits}b}" + b = f"{b_mantissa:0{m_bits}b}" + + a_fp = generate_FP(e_bits, str(a_sign), a_exp_norm, a, e_bias) + b_fp = generate_FP(e_bits, str(b_sign), b_exp_norm, b, e_bias) + + run_and_store_test_vector( + f"{OP_DIV}_{rounding_mode}_{b_fp}_{a_fp}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f + ) + return a_fp, b_fp + + +def tests_div_1_2(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + m_bits = MANTISSA_BITS[precision] + min_exp = UNBIASED_EXP[precision][0] + + min_sn = min_exp - m_bits + + p_sn_exp = random.randint(min_sn, min_exp - 1) + n_sn_exp = random.randint(min_sn, min_exp - 1) + + # Random SN: G = 0, R = 0, S = 1 + div_grs_gen(test_f, cover_f, "001", min_sn, precision, rounding_mode, p_sn_exp, "0", "positive") + div_grs_gen(test_f, cover_f, "001", min_sn, precision, rounding_mode, n_sn_exp, "1", "negative") + + +def divTests(test_f: TextIO, cover_f: TextIO) -> None: + # for precision in FLOAT_FMTS: + precision = FMT_HALF + for rounding_mode in ROUNDING_MODES: + tests_div_1_2(precision, rounding_mode, test_f, cover_f) + + +def tests_add_sub_1_2(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + m_bits = MANTISSA_BITS[precision] + e_bits = EXPONENT_BITS[precision] + e_bias = EXPONENT_BIAS[precision] + sn_exp = UNBIASED_EXP[precision][0] - 1 + + maxMant = (1 << m_bits) - 1 + # List for each subNormal: + # operation, sign + # add posSN, add negSN, sub posSN, sub negSN + testList = [["add", 0], ["add", 1], ["sub", 0], ["sub", 1]] + for test in testList: + sign = str(test[1]) + if test[0] == "add": + op = OP_ADD + seed("b5" + op + precision + rounding_mode + "a" + str(test[1])) + a = random.randint(1, maxMant // 2) + seed("b5" + op + precision + rounding_mode + "b" + str(test[1])) + b = random.randint(1, maxMant // 2) + else: + op = OP_SUB + seed("b5" + op + precision + rounding_mode + "b" + str(test[1])) + b = random.randint(1, maxMant // 2) + seed("b5" + op + precision + rounding_mode + "a" + str(test[1])) + a = random.randint(b, maxMant) + a = f"{a:0{m_bits}b}" + b = f"{b:0{m_bits}b}" + a_fp = generate_FP(e_bits, sign, sn_exp, a, e_bias) + b_fp = generate_FP(e_bits, sign, sn_exp, b, e_bias) + run_and_store_test_vector( + f"{op}_{rounding_mode}_{a_fp}_{b_fp}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f + ) + + +def tests_add_sub_3_4(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + m_bits = MANTISSA_BITS[precision] + e_bits = EXPONENT_BITS[precision] + e_bias = EXPONENT_BIAS[precision] + sn_exp = UNBIASED_EXP[precision][0] - 1 + + maxMant = (1 << m_bits) - 1 + + # The IBM paper only has +minSN for 3 and -minSN for 4 + testList = [["add", 0], ["add", 1], ["sub", 0], ["sub", 1]] + for test in testList: + seed("b5" + str(test[0]) + precision + rounding_mode + "a" + str(test[1])) + a = random.randint(2, maxMant) + seed("b5" + str(test[0]) + precision + rounding_mode + "b" + str(test[1])) + b = a - 1 + if test[0] == "add": + op = OP_ADD + a_sign = test[1] + b_sign = (int(test[1]) + 1) % 2 + else: + op = OP_SUB + a_sign = test[1] + b_sign = a_sign + + a = f"{a:0{m_bits}b}" + b = f"{b:0{m_bits}b}" + + a_fp = generate_FP(e_bits, str(a_sign), sn_exp, a, e_bias) + b_fp = generate_FP(e_bits, str(b_sign), sn_exp, b, e_bias) + + run_and_store_test_vector( + f"{op}_{rounding_mode}_{a_fp}_{b_fp}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f + ) + + +def tests_add_sub_5_6(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + m_bits = MANTISSA_BITS[precision] + e_bits = EXPONENT_BITS[precision] + e_bias = EXPONENT_BIAS[precision] + sn_exp = UNBIASED_EXP[precision][0] - 1 + + maxMant = (1 << m_bits) - 1 + + # Add operations: + normDist = -3 + while normDist <= 3: + for sign in [0, 1]: + seed("b5" + "OP_ADD" + precision + rounding_mode + "a" + str(normDist) + str(sign)) + a = random.randint(3, maxMant) + b = maxMant - a + 1 + normDist + a_fp = generate_FP(e_bits, str(sign), sn_exp, f"{a:0{m_bits}b}", e_bias) + b_fp = generate_FP(e_bits, str(sign), sn_exp, f"{b:0{m_bits}b}", e_bias) + run_and_store_test_vector( + f"{OP_ADD}_{rounding_mode}_{a_fp}_{b_fp}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", + test_f, + cover_f, + ) + normDist += 1 + + # Subtraction operations + normDist = -3 + while normDist <= 3: + for sign in [0, 1]: + seed("b5" + "OP_SUB" + precision + rounding_mode + "a" + str(normDist) + str(sign)) + a = random.randint(3, maxMant - 3) + b = a + normDist + b_exp = sign + sn_exp + a_exp = sn_exp + ((sign + 1) % 2) + a_fp = generate_FP(e_bits, str(0), a_exp, f"{a:0{m_bits}b}", e_bias) + b_fp = generate_FP(e_bits, str(0), b_exp, f"{b:0{m_bits}b}", e_bias) + run_and_store_test_vector( + f"{OP_SUB}_{rounding_mode}_{a_fp}_{b_fp}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", + test_f, + cover_f, + ) + normDist += 1 + + +def tests_add_sub_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + m_bits = MANTISSA_BITS[precision] + e_bits = EXPONENT_BITS[precision] + e_bias = EXPONENT_BIAS[precision] + sn_exp = UNBIASED_EXP[precision][0] - 1 + + maxMant = (1 << m_bits) - 1 + for i in range(1, 7): + for op in [OP_ADD, OP_SUB]: + a_exp = sn_exp + b_exp = sn_exp + i + seed("b5" + str(op) + precision + rounding_mode + "a" + str(i)) + a_mant = random.randint(0, maxMant) + a_sign = str(random.randint(0, 1)) + seed("b5" + str(op) + precision + rounding_mode + "b" + str(i)) + b_mant = random.randint(a_mant, maxMant) + b_sign = str(random.randint(0, 1)) + a_fp = generate_FP(e_bits, a_sign, a_exp, f"{a_mant:0{m_bits}b}", e_bias) + b_fp = generate_FP(e_bits, b_sign, b_exp, f"{b_mant:0{m_bits}b}", e_bias) + run_and_store_test_vector( + f"{op}_{rounding_mode}_{a_fp}_{b_fp}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f + ) + + +def addSubTests(test_f: TextIO, cover_f: TextIO) -> None: + for precision in FLOAT_FMTS: + for rounding_mode in ROUNDING_MODES: + tests_add_sub_1_2(precision, rounding_mode, test_f, cover_f) + tests_add_sub_3_4(precision, rounding_mode, test_f, cover_f) + tests_add_sub_5_6(precision, rounding_mode, test_f, cover_f) + tests_add_sub_9(precision, rounding_mode, test_f, cover_f) + + +def main() -> None: + with ( + Path("./tests/testvectors/B5_tv.txt").open("w") as test_f, + Path("./tests/covervectors/B5_cv.txt").open("w") as cover_f, + ): convertTests(test_f, cover_f) + multiplyTests(test_f, cover_f) + addSubTests(test_f, cover_f) + fmaTests(test_f, cover_f) + divTests(test_f, cover_f) + + # Set your input and output file names here + # input_filename = "./tests/covervectors/B5_cv.txt" + # output_filename = "./tests/readable/decoded_results.txt" if __name__ == "__main__": From e11d3b6553e7e61378ef05929d49b9ec0cd6e63d Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Wed, 1 Apr 2026 00:18:58 -0700 Subject: [PATCH 15/21] updated division tests --- src/cover_float/testgen/B5.py | 337 +++++++++++++++++++++++++--------- 1 file changed, 253 insertions(+), 84 deletions(-) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 3ac24f5..6787cf6 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -7,7 +7,6 @@ from random import seed from typing import TextIO -# from cover_float.testgen import softfloat_parser from cover_float.common.constants import ( EXPONENT_BIAS, EXPONENT_BITS, @@ -283,7 +282,7 @@ def convertTests(test_f: TextIO, cover_f: TextIO) -> None: tests_conversion_9(lp, hp, rounding_mode, test_f, cover_f) -def genSpecExp(precision: str, target: int, hashString: str) -> tuple[int, int]: +def genSpecExp_mul(precision: str, target: int, hashString: str) -> tuple[int, int]: hashval = reproducible_hash(hashString) seed(hashval) m_bits = MANTISSA_BITS[precision] @@ -296,7 +295,7 @@ def genSpecExp(precision: str, target: int, hashString: str) -> tuple[int, int]: return (a_exp, b_exp) -def genSpecExp_div(precision: str, target: int, hashString: str) -> tuple[int, int]: +def genSpecExp_div(precision: str, target: int, hashString: str, grs_int: int) -> tuple[int, int]: hashval = reproducible_hash(hashString) seed(hashval) m_bits = MANTISSA_BITS[precision] @@ -304,12 +303,16 @@ def genSpecExp_div(precision: str, target: int, hashString: str) -> tuple[int, i max_exp = UNBIASED_EXP[precision][1] # Absolute lowest effective exponent (subnormal floor) - min_sn = min_exp - m_bits + min_sn = min_exp - m_bits + 1 # Mathematical rule for Division: target = a_exp - b_exp # Therefore: b_exp = a_exp - target lower_bound = max(min_sn, min_sn + target) + + if grs_int == 7 or grs_int == 5: + lower_bound = max(min_exp, min_sn + target) # No Subnormal Values, so mantissa has same # of bits + upper_bound = min(max_exp, max_exp + target) if lower_bound > upper_bound: @@ -318,21 +321,12 @@ def genSpecExp_div(precision: str, target: int, hashString: str) -> tuple[int, i a_exp = random.randint(lower_bound, upper_bound) b_exp = a_exp - target - return (a_exp, b_exp) + large_exp = max(a_exp, b_exp) + small_exp = min(b_exp, a_exp) + return (small_exp, large_exp) -def genRandExp(precision: str, max_exp: int, hashString: str) -> tuple[int, int]: - hashval = reproducible_hash(hashString) - seed(hashval) - m_bits = MANTISSA_BITS[precision] - min_exp = UNBIASED_EXP[precision][0] - - smallest_res_exp = min_exp - (2 * m_bits) - target = random.randint(smallest_res_exp, max_exp) - return genSpecExp(precision, target, hashString) - - -def get_grs_mant(precision: str, a_exp: int, b_exp: int, hashString: str, grs: str) -> tuple[str, str]: +def get_grs_mant(operation: str, precision: str, a_exp: int, b_exp: int, hashString: str, grs: str) -> tuple[str, str]: m_bits = MANTISSA_BITS[precision] e_min = UNBIASED_EXP[precision][0] min_sn = e_min - m_bits @@ -356,22 +350,63 @@ def get_grs_mant(precision: str, a_exp: int, b_exp: int, hashString: str, grs: s b_rBit = False if grs_int == 6: if a_exp == min_sn or b_exp == min_sn: # If exp = min_sn, then you can't have the rBit - if a_exp == min_sn and b_exp == min_sn: - raise ValueError("a_exp and b_exp can't both be min_sn") - elif a_exp == min_sn: - b_rBit = True - elif b_exp == min_sn: + if operation == OP_MUL: + if a_exp == min_sn and b_exp == min_sn: + raise ValueError("a_exp and b_exp can't both be min_sn") + elif a_exp == min_sn: + b_rBit = True + elif b_exp == min_sn: + a_rBit = True + else: a_rBit = True else: # Random selection otherwise - a_rBit = random.randint(0, 1) == 1 # If 1 is randomly selected, then a_rBit = True - b_rBit = not a_rBit + if operation == OP_MUL: + a_rBit = random.randint(0, 1) == 1 # If 1 is randomly selected, then a_rBit = True + b_rBit = not a_rBit + else: + a_rBit = True if a_rBit: - a_mantissa += 1 << a_bits_left - 1 + a_mantissa += 1 << (a_bits_left - 1) elif b_rBit: - b_mantissa += 1 << b_bits_left - 1 + b_mantissa += 1 << (b_bits_left - 1) + + elif (grs_int == 7 and operation == OP_DIV) or (grs_int == 5 and operation == OP_DIV): + a_max = (1 << (a_bits_left + 1)) - 1 + # a_min = 1 << a_bits_left # With hidden 1 + + b_min = 1 << b_bits_left + b_max = (1 << (b_bits_left + 1)) - 1 + + a_int = 0 + b_int = 0 + + if grs_int == 7 or grs_int == 5: # G = 1, R = 1, S = 1 + # b and a mantissas have the same range + b_min_range = b_min + b_max_range = (2 * b_max) // 3 + + small_int = random.randint(b_min_range, b_max_range) + + a_min_range = (3 * small_int) // 2 + a_max_range = a_max + + large_int = random.randint(a_min_range, a_max_range) - # grs 2 and 4 want need 0s after specified 1 bit - if grs_int != 2 and grs_int != 6 and grs_int != 4: + if grs_int == 7: + a_int = large_int + b_int = small_int + else: + a_int = small_int + b_int = large_int + + a_mantissa = a_int - (1 << a_bits_left) # Remove hidden 1 + b_mantissa = b_int - (1 << b_bits_left) + + if grs_int == 3 and operation == OP_DIV: + a_mantissa = random.randint(1, (1 << a_bits_left) - 1) + b_mantissa = random.randint(1, (1 << b_bits_left) - 1) + + if grs_int != 2 and grs_int != 6 and grs_int != 4 and operation == OP_MUL: while not met_conditions: seed(cycles_attempted) # Make deterministic @@ -395,12 +430,12 @@ def get_grs_mant(precision: str, a_exp: int, b_exp: int, hashString: str, grs: s maxNorm = 1 << (product_bits - 1) # Really the smallest nonNorm if product_a_b < maxNorm: - if grs_int == 5 or grs_int == 8: + if grs_int == 5 or grs_int == 7: subtract_g_bit = product_a_b - (1 << decimal_bit) subtract_r_bit = subtract_g_bit - (1 << (decimal_bit - 1)) if grs_int == 5: met_conditions = subtract_r_bit < 0 - elif grs_int == 8: + elif grs_int == 7: met_conditions = subtract_r_bit > 0 else: met_conditions = True @@ -424,7 +459,8 @@ def get_grs_mant(precision: str, a_exp: int, b_exp: int, hashString: str, grs: s return (bin_a_mantissa, bin_b_mantissa) -def mul_grs_gen( +def mul_div_grs_gen( + operation: str, precision: str, rounding_mode: str, grs: str, @@ -440,6 +476,7 @@ def mul_grs_gen( # g_exp is needed for going below normal, getting specific subnorm results e_bits = EXPONENT_BITS[precision] + m_bits = MANTISSA_BITS[precision] e_bias = EXPONENT_BIAS[precision] min_exp = UNBIASED_EXP[precision][0] sn_exp = min_exp - 1 @@ -454,12 +491,18 @@ def mul_grs_gen( b_sign = (a_sign + 1) % 2 grs_int = int(grs, 2) + + target_exp = first_bit if grs_int == 1: - a_exp, b_exp = genRandExp(precision, first_bit, hashString + grs + sign) - else: - a_exp, b_exp = genSpecExp(precision, first_bit, hashString + grs + sign) + smallest_res_exp = min_exp - (2 * m_bits) + target_exp = random.randint(smallest_res_exp, g_exp) - a_mant, b_mant = get_grs_mant(precision, a_exp, b_exp, hashString + grs + sign, grs) + if operation == OP_MUL: + a_exp, b_exp = genSpecExp_mul(precision, target_exp, hashString + grs + sign) + else: # operation == OP_DIV + a_exp, b_exp = genSpecExp_div(precision, target_exp, hashString, grs_int) + + a_mant, b_mant = get_grs_mant(operation, precision, a_exp, b_exp, hashString + grs + sign, grs) # Normalize exponents a_exp = max(a_exp, sn_exp) @@ -470,7 +513,7 @@ def mul_grs_gen( if genTests: run_and_store_test_vector( - f"{OP_MUL}_{rounding_mode}_{a}_{b}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f + f"{operation}_{rounding_mode}_{a}_{b}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f ) return (a, b) @@ -483,8 +526,8 @@ def tests_multiply_1_2(precision: str, rounding_mode: str, test_f: TextIO, cover min_exp = UNBIASED_EXP[precision][0] sn_exp = min_exp - 1 - mul_grs_gen(precision, rounding_mode, "001", sn_exp, "0", test_f, cover_f, "1/2", True) # Positive Test - mul_grs_gen(precision, rounding_mode, "001", sn_exp, "1", test_f, cover_f, "1/2", True) # Negative Test + mul_div_grs_gen(OP_MUL, precision, rounding_mode, "001", sn_exp, "0", test_f, cover_f, "1/2", True) # Positive Test + mul_div_grs_gen(OP_MUL, precision, rounding_mode, "001", sn_exp, "1", test_f, cover_f, "1/2", True) # Negative Test def tests_multiply_3_4(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: @@ -497,43 +540,79 @@ def tests_multiply_3_4(precision: str, rounding_mode: str, test_f: TextIO, cover minSNPos = min_exp - m_bits # Treating the minSN as normalized # minSN - 3 ulp G = 0, R = 0, S = 1 - mul_grs_gen(precision, rounding_mode, "001", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True) # Positive Test - mul_grs_gen(precision, rounding_mode, "001", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True) # Negative Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "001", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True + ) # Positive Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "001", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True + ) # Negative Test # minSN - 2 ulp G = 0, R = 1, S = 0 - mul_grs_gen(precision, rounding_mode, "010", minSNPos, "0", test_f, cover_f, "minSN-2ulppos", True) # Positive Test - mul_grs_gen(precision, rounding_mode, "010", minSNPos, "1", test_f, cover_f, "minSN-2ulpneg", True) # Negative Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "010", minSNPos, "0", test_f, cover_f, "minSN-2ulppos", True + ) # Positive Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "010", minSNPos, "1", test_f, cover_f, "minSN-2ulpneg", True + ) # Negative Test # minSN - 1 ulp G = 0, R = 1, S = 1 - mul_grs_gen(precision, rounding_mode, "011", minSNPos, "0", test_f, cover_f, "minSN-1ulppos", True) # Positive Test - mul_grs_gen(precision, rounding_mode, "011", minSNPos, "1", test_f, cover_f, "minSN-1ulpneg", True) # Negative Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "011", minSNPos, "0", test_f, cover_f, "minSN-1ulppos", True + ) # Positive Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "011", minSNPos, "1", test_f, cover_f, "minSN-1ulpneg", True + ) # Negative Test # minSN G = 1, R = 0, S = 0 - mul_grs_gen(precision, rounding_mode, "100", minSNPos, "0", test_f, cover_f, "minSNpos", True) # Positive Test - mul_grs_gen(precision, rounding_mode, "100", minSNPos, "1", test_f, cover_f, "minSNneg", True) # Negative Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "100", minSNPos, "0", test_f, cover_f, "minSNpos", True + ) # Positive Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "100", minSNPos, "1", test_f, cover_f, "minSNneg", True + ) # Negative Test # minSN + 1 ulp G = 1, R = 0, S = 1 - mul_grs_gen(precision, rounding_mode, "101", minSNPos, "0", test_f, cover_f, "minSN+1ulppos", True) # Positive Test - mul_grs_gen(precision, rounding_mode, "101", minSNPos, "1", test_f, cover_f, "minSN+1ulpneg", True) # Negative Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "101", minSNPos, "0", test_f, cover_f, "minSN+1ulppos", True + ) # Positive Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "101", minSNPos, "1", test_f, cover_f, "minSN+1ulpneg", True + ) # Negative Test # minSN + 2 ulp G = 1, R = 1, S = 0 - mul_grs_gen(precision, rounding_mode, "110", minSNPos, "0", test_f, cover_f, "minSN+2ulppos", True) # Positive Test - mul_grs_gen(precision, rounding_mode, "110", minSNPos, "1", test_f, cover_f, "minSN+2ulpneg", True) # Negative Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "110", minSNPos, "0", test_f, cover_f, "minSN+2ulppos", True + ) # Positive Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "110", minSNPos, "1", test_f, cover_f, "minSN+2ulpneg", True + ) # Negative Test # minSN + 3 ulp G = 1, R = 1, S = 1 - mul_grs_gen(precision, rounding_mode, "111", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True) # Positive Test - mul_grs_gen(precision, rounding_mode, "111", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True) # Negative Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "111", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True + ) # Positive Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "111", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True + ) # Negative Test def tests_multiply_5_6(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: min_exp = UNBIASED_EXP[precision][0] # MinNorm - 3ulp - mul_grs_gen(precision, rounding_mode, "100", min_exp, "0", test_f, cover_f, "minExp", True) # Positive Test - mul_grs_gen(precision, rounding_mode, "100", min_exp, "1", test_f, cover_f, "minExp", True) # Negative Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "100", min_exp, "0", test_f, cover_f, "minExp", True + ) # Positive Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "100", min_exp, "1", test_f, cover_f, "minExp", True + ) # Negative Test # MinNorm - mul_grs_gen(precision, rounding_mode, "100", min_exp, "0", test_f, cover_f, "minExp", True) # Positive Test - mul_grs_gen(precision, rounding_mode, "100", min_exp, "1", test_f, cover_f, "minExp", True) # Negative Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "100", min_exp, "0", test_f, cover_f, "minExp", True + ) # Positive Test + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "100", min_exp, "1", test_f, cover_f, "minExp", True + ) # Negative Test def tests_multiply_7_8(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: @@ -542,8 +621,8 @@ def tests_multiply_7_8(precision: str, rounding_mode: str, test_f: TextIO, cover target_exp = min_exp - m_bits - mul_grs_gen(precision, rounding_mode, "011", target_exp, "0", test_f, cover_f, "1/2 pos", True) - mul_grs_gen(precision, rounding_mode, "011", target_exp, "1", test_f, cover_f, "1/2 neg", True) + mul_div_grs_gen(OP_MUL, precision, rounding_mode, "011", target_exp, "0", test_f, cover_f, "1/2 pos", True) + mul_div_grs_gen(OP_MUL, precision, rounding_mode, "011", target_exp, "1", test_f, cover_f, "1/2 neg", True) def tests_multiply_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: @@ -557,17 +636,20 @@ def tests_multiply_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f seed("b5" + OP_MUL + precision + rounding_mode + str(target_exp)) sign = str(random.randint(0, 1)) - mul_grs_gen(precision, rounding_mode, "011", target_exp, sign, test_f, cover_f, str(target_exp), True) + mul_div_grs_gen( + OP_MUL, precision, rounding_mode, "011", target_exp, sign, test_f, cover_f, str(target_exp), True + ) def multiplyTests(test_f: TextIO, cover_f: TextIO) -> None: - for precision in FLOAT_FMTS: - for rounding_mode in ROUNDING_MODES: - tests_multiply_1_2(precision, rounding_mode, test_f, cover_f) - tests_multiply_3_4(precision, rounding_mode, test_f, cover_f) - tests_multiply_5_6(precision, rounding_mode, test_f, cover_f) - tests_multiply_7_8(precision, rounding_mode, test_f, cover_f) - tests_multiply_9(precision, rounding_mode, test_f, cover_f) + # for precision in FLOAT_FMTS: + precision = FMT_HALF + for rounding_mode in ROUNDING_MODES: + tests_multiply_1_2(precision, rounding_mode, test_f, cover_f) + tests_multiply_3_4(precision, rounding_mode, test_f, cover_f) + tests_multiply_5_6(precision, rounding_mode, test_f, cover_f) + tests_multiply_7_8(precision, rounding_mode, test_f, cover_f) + tests_multiply_9(precision, rounding_mode, test_f, cover_f) def fma_gen( @@ -603,8 +685,17 @@ def fma_gen( c_sign = (op_add_sign + addend_sign) % 2 # Generate the multiplication testvectors - a, b = mul_grs_gen( - precision, rounding_mode, product_grs, product_exponent, str(mul_sign), test_f, cover_f, hashEnding, False + a, b = mul_div_grs_gen( + OP_MUL, + precision, + rounding_mode, + product_grs, + product_exponent, + str(mul_sign), + test_f, + cover_f, + hashEnding, + False, ) # Generate the addition testvector @@ -802,10 +893,7 @@ def fmaTests(test_f: TextIO, cover_f: TextIO) -> None: tests_fma_9(operation, precision, rounding_mode, test_f, cover_f) -# def get_div_mant(precision: str, a_exp: int, b_exp: int, hashString: str, grs: str) -> str: - - -def div_grs_gen( +def div_grs_mant( test_f: TextIO, cover_f: TextIO, grs: str, @@ -822,7 +910,7 @@ def div_grs_gen( min_exp = UNBIASED_EXP[precision][0] # Determine exponents, subtract to target_exp - possible_exponents = genSpecExp_div(precision, target_exp, hashString) + possible_exponents = genSpecExp_div(precision, target_exp, hashString, int(grs, 2)) a_exp = max(possible_exponents) b_exp = min(possible_exponents) @@ -864,24 +952,109 @@ def div_grs_gen( def tests_div_1_2(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + min_exp = UNBIASED_EXP[precision][0] + + # Random SN: G = 0, R = 0, S = 1 + mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", min_exp + 1, "0", test_f, cover_f, "positive", True) + mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", min_exp + 1, "0", test_f, cover_f, "positive", True) + + +def tests_div_3_4(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: m_bits = MANTISSA_BITS[precision] min_exp = UNBIASED_EXP[precision][0] - min_sn = min_exp - m_bits + minSNPos = min_exp - m_bits # Treating the minSN as normalized - p_sn_exp = random.randint(min_sn, min_exp - 1) - n_sn_exp = random.randint(min_sn, min_exp - 1) + # minSN - 3 ulp G = 0, R = 0, S = 1 + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "001", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True + ) # Positive Test + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "001", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True + ) # Negative Test - # Random SN: G = 0, R = 0, S = 1 - div_grs_gen(test_f, cover_f, "001", min_sn, precision, rounding_mode, p_sn_exp, "0", "positive") - div_grs_gen(test_f, cover_f, "001", min_sn, precision, rounding_mode, n_sn_exp, "1", "negative") + # minSN - 2 ulp G = 0, R = 1, S = 0 + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "010", minSNPos, "0", test_f, cover_f, "minSN-2ulppos", True + ) # Positive Test + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "010", minSNPos, "1", test_f, cover_f, "minSN-2ulpneg", True + ) # Negative Test + + # minSN - 1 ulp G = 0, R = 1, S = 1 + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "011", minSNPos, "0", test_f, cover_f, "minSN-1ulppos", True + ) # Positive Test + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "011", minSNPos, "1", test_f, cover_f, "minSN-1ulpneg", True + ) # Negative Test + + # minSN G = 1, R = 0, S = 0 + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "100", minSNPos, "0", test_f, cover_f, "minSNpos", True + ) # Positive Test + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "100", minSNPos, "1", test_f, cover_f, "minSNneg", True + ) # Negative Test + + # minSN + 1 ulp G = 1, R = 0, S = 1 + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "101", minSNPos, "0", test_f, cover_f, "minSN+1ulppos", True + ) # Positive Test + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "101", minSNPos, "1", test_f, cover_f, "minSN+1ulpneg", True + ) # Negative Test + + # minSN + 2 ulp G = 1, R = 1, S = 0 + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "110", minSNPos, "0", test_f, cover_f, "minSN+2ulppos", True + ) # Positive Test + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "110", minSNPos, "1", test_f, cover_f, "minSN+2ulpneg", True + ) # Negative Test + + # minSN + 3 ulp G = 1, R = 1, S = 1 + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "111", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True + ) # Positive Test + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "111", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True + ) # Negative Test + + +def tests_div_7_8(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + m_bits = MANTISSA_BITS[precision] + min_exp = UNBIASED_EXP[precision][0] + + minSNPos = min_exp - m_bits # Treating the minSN as normalized + + mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", minSNPos, "0", test_f, cover_f, "minSN", True) + mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", minSNPos, "1", test_f, cover_f, "minSN", True) + + +def tests_div_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + min_exp = UNBIASED_EXP[precision][0] + + min_exp_range = min_exp - 1 # SN exp, or minNorm.exp + max_exp_range = min_exp_range + 5 + + for target_exp in range(min_exp_range, max_exp_range + 1): # Because end is exclusive + seed("b5" + OP_DIV + precision + rounding_mode + str(target_exp)) + sign = str(random.randint(0, 1)) + + mul_div_grs_gen( + OP_DIV, precision, rounding_mode, "011", target_exp + 1, sign, test_f, cover_f, str(target_exp), True + ) def divTests(test_f: TextIO, cover_f: TextIO) -> None: # for precision in FLOAT_FMTS: precision = FMT_HALF for rounding_mode in ROUNDING_MODES: - tests_div_1_2(precision, rounding_mode, test_f, cover_f) + # tests_div_1_2(precision, rounding_mode, test_f, cover_f) + tests_div_3_4(precision, rounding_mode, test_f, cover_f) + # tests_div_7_8(precision, rounding_mode, test_f, cover_f) + # tests_div_9(precision, rounding_mode, test_f, cover_f) def tests_add_sub_1_2(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: @@ -1040,10 +1213,6 @@ def main() -> None: fmaTests(test_f, cover_f) divTests(test_f, cover_f) - # Set your input and output file names here - # input_filename = "./tests/covervectors/B5_cv.txt" - # output_filename = "./tests/readable/decoded_results.txt" - if __name__ == "__main__": main() From bdad913e1ee9f21cc51ed8684d16a941b3f22070 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Sat, 18 Apr 2026 15:23:39 -0700 Subject: [PATCH 16/21] Test 9 Multiply at 100% --- .gitignore | 1 + src/cover_float/testgen/B5.py | 39 ++++++++++++++++------------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index a37baab..3077e88 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ tests/covervectors/B*_cv.txt tests/testvectors/B*_tv.txt coverage/covergroups/bins_templates/generated tests/readable/B*_parsed.txt +src/cover_float/testgen/softfloat_parser.py diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 6787cf6..d29a317 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -26,27 +26,21 @@ OP_FNMSUB, OP_MUL, OP_SUB, + ROUND_MAX, + ROUND_MIN, + ROUND_MINMAG, + ROUND_NEAR_EVEN, + ROUND_NEAR_MAXMAG, UNBIASED_EXP, ) from cover_float.common.util import reproducible_hash from cover_float.reference import run_and_store_test_vector B5_FMTS = [FMT_QUAD, FMT_DOUBLE, FMT_SINGLE, FMT_BF16, FMT_HALF] -ROUNDING_MODES = ["00", "01", "02", "03", "04", "05"] +ROUNDING_MODES = [ROUND_NEAR_EVEN, ROUND_MINMAG, ROUND_MIN, ROUND_MAX, ROUND_NEAR_MAXMAG] FMA_OPS = [OP_FMADD, OP_FMSUB, OP_FNMADD, OP_FNMSUB] -# def generate_FP( -# input_e_bitwidth: int, input_sign: str, input_exponent: int, input_mantissa: str, input_bias: int -# ) -> str: -# exponent = f"{input_exponent + input_bias:0{input_e_bitwidth}b}" -# complete = input_sign + exponent + input_mantissa -# fp_complete = format(int(complete, 2), "X") - - -# return fp_complete - - def generate_FP( input_e_bitwidth: int, input_sign: str, input_exponent: int, input_mantissa: str, input_bias: int ) -> str: @@ -627,14 +621,17 @@ def tests_multiply_7_8(precision: str, rounding_mode: str, test_f: TextIO, cover def tests_multiply_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: min_exp = UNBIASED_EXP[precision][0] + e_bias = EXPONENT_BIAS[precision] # all values from minNorm.exp to minNorm.exp + 5 - min_exp_range = min_exp - 1 # SN exp, or minNorm.exp + min_exp_range = min_exp + 1 # minNorm.exp + 1, we want it randomized, so add 1 and lsb = 0, r = 1, s = 1 max_exp_range = min_exp_range + 5 for target_exp in range(min_exp_range, max_exp_range + 1): # Because end is exclusive seed("b5" + OP_MUL + precision + rounding_mode + str(target_exp)) sign = str(random.randint(0, 1)) + # print("Unbiased Target_exp:", target_exp) + print("Biased Target_exp:", target_exp + e_bias) mul_div_grs_gen( OP_MUL, precision, rounding_mode, "011", target_exp, sign, test_f, cover_f, str(target_exp), True @@ -642,14 +639,14 @@ def tests_multiply_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f def multiplyTests(test_f: TextIO, cover_f: TextIO) -> None: - # for precision in FLOAT_FMTS: - precision = FMT_HALF - for rounding_mode in ROUNDING_MODES: - tests_multiply_1_2(precision, rounding_mode, test_f, cover_f) - tests_multiply_3_4(precision, rounding_mode, test_f, cover_f) - tests_multiply_5_6(precision, rounding_mode, test_f, cover_f) - tests_multiply_7_8(precision, rounding_mode, test_f, cover_f) - tests_multiply_9(precision, rounding_mode, test_f, cover_f) + for precision in FLOAT_FMTS: + # precision = FMT_SINGLE #TODO: Alter to have all precisions + for rounding_mode in ROUNDING_MODES: + tests_multiply_1_2(precision, rounding_mode, test_f, cover_f) + tests_multiply_3_4(precision, rounding_mode, test_f, cover_f) + tests_multiply_5_6(precision, rounding_mode, test_f, cover_f) + tests_multiply_7_8(precision, rounding_mode, test_f, cover_f) + tests_multiply_9(precision, rounding_mode, test_f, cover_f) def fma_gen( From d41bf3802ce1674fbe4d435d3027f332f0b32b7e Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Sun, 26 Apr 2026 23:04:11 -0700 Subject: [PATCH 17/21] debugging multiplication generation --- src/cover_float/testgen/B5.py | 264 +++++++++++++++++++++++++++------- 1 file changed, 216 insertions(+), 48 deletions(-) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index d29a317..2f5ad55 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -453,6 +453,153 @@ def get_grs_mant(operation: str, precision: str, a_exp: int, b_exp: int, hashStr return (bin_a_mantissa, bin_b_mantissa) +def factor_mul_gen( + precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO, grs_pattern: str, sign: str, genTests: bool +) -> tuple[str, str]: + m_bits = MANTISSA_BITS[precision] + e_min = UNBIASED_EXP[precision][0] - 1 + e_bits = EXPONENT_BITS[precision] + e_bias = EXPONENT_BIAS[precision] + + target_list = { + # Above MinNorm + "001": { + FMT_BF16: (8 + 1) * (8**2 - 8 + 1), + FMT_HALF: ((2**4) + 1) * ((2**4) ** 2 - (2**4) + 1), + FMT_SINGLE: ((2**5) + 1) * ((2**5) ** 4 - (2**5) ** 3 + (2**5) ** 2 - (2**5) + 1), + FMT_DOUBLE: ((2**11) + 1) * ((2**11) ** 4 - (2**11) ** 3 + (2**11) ** 2 - (2**11) + 1), + FMT_QUAD: ((2**38) + 1) * ((2**76) - (2**38) + 1), + }, + "010": { + FMT_BF16: 2056, + FMT_HALF: (2**11) + 1, + FMT_SINGLE: ((2**8) + 1) * ((2**8) ** 2 - (2**8) + 1), + FMT_DOUBLE: (2**53) + 1, + FMT_QUAD: (2**113) + 1, + }, + "011": { + FMT_BF16: 2**9 + 2 + 1, + FMT_HALF: 2**13 + 4 + 3, + FMT_SINGLE: (2**26) + 7, + FMT_DOUBLE: 2**55 + 7, + FMT_QUAD: 2**120 + 125, + }, + # Below MinNorm + "111": { + FMT_BF16: (2**14) - 1, + FMT_HALF: (2**20) - 1, + FMT_SINGLE: (2**46) - 1, + FMT_DOUBLE: (2**104) - 1, + FMT_QUAD: (2**224) - 1, + }, # - 1 ulp + "110": { + FMT_BF16: (2 ** (m_bits + 1)) - 1, + FMT_HALF: (2 ** (m_bits + 1)) - 1, + FMT_SINGLE: (2 ** (m_bits + 1)) - 1, + FMT_DOUBLE: (2 ** (m_bits + 1)) - 1, + FMT_QUAD: (2 ** (m_bits + 1)) - 1, + }, # - 2ulp + "101": { + FMT_BF16: 19 * 107, + FMT_HALF: 233 * 281, + FMT_SINGLE: 479 * 70051, + FMT_DOUBLE: 497401731493 * 36217, + FMT_QUAD: 613 * 33881219305284356466756909162937, + }, # - 3 ulp + } + + factor_list = { + # Above MinNorm + "001": { + FMT_BF16: (8 + 1), + FMT_HALF: ((2**4) + 1), + FMT_SINGLE: ((2**5) + 1), + FMT_DOUBLE: ((2**11) + 1), + FMT_QUAD: ((2**38) + 1), + }, + "010": {FMT_BF16: 257, FMT_HALF: 683, FMT_SINGLE: ((2**8) + 1), FMT_DOUBLE: 321, FMT_QUAD: 491003369344660409}, + "011": {FMT_BF16: 103, FMT_HALF: 9, FMT_SINGLE: 23 * 29, FMT_DOUBLE: 9 * 25, FMT_QUAD: 1099511627781}, + # Below MinNorm + "111": { + FMT_BF16: (2**7) - 1, + FMT_HALF: (2**10) - 1, + FMT_SINGLE: (2**23) - 1, + FMT_DOUBLE: (2**52) - 1, + FMT_QUAD: (2**112) - 1, + }, + "110": { + FMT_BF16: (4**2) - 1, + FMT_HALF: 23, + FMT_SINGLE: (2**12) - 1, + FMT_DOUBLE: 6361, + FMT_QUAD: 1066818132868207, + }, + "101": { + FMT_BF16: 19, + FMT_HALF: 233, + FMT_SINGLE: 479, + FMT_DOUBLE: 36217, + FMT_QUAD: 33881219305284356466756909162937, + }, + } + print(precision) + # Randomize the sign bit + if sign == "0": + a_sign = random.randint(0, 1) + b_sign = a_sign + else: + a_sign = random.randint(0, 1) + b_sign = (a_sign + 1) % 2 + + target_int = target_list[grs_pattern][precision] + factor_1 = factor_list[grs_pattern][precision] + factor_2 = target_int // factor_1 # Integers have unlimited precision, must use integer division + + if target_int == 1 and factor_1 == 1: + return ("0", "0") + + a_int = int(max(factor_1, factor_2)) # Ensure a is the largest value + b_int = int(min(factor_1, factor_2)) + + a_bin = format(a_int, "b") + b_bin = format(b_int, "b") + + a_bits = len(a_bin) + b_bits = len(b_bin) + + exp_offset = a_bits - b_bits + + # Subtract Hidden 1 + a_int -= 1 << (a_bits - 1) + b_int -= 1 << (b_bits - 1) + + a_trailing_zeros = m_bits - a_bits + 1 + b_trailing_zeros = m_bits - b_bits + 1 + + a_int *= 2 ** max(a_trailing_zeros, 0) + b_int *= 2 ** max(b_trailing_zeros, 0) + + a_bin = f"{a_int:0{m_bits}b}" + b_bin = f"{b_int:0{m_bits}b}" + + a_exp = int((e_min + exp_offset) / 2) + b_exp = int(a_exp - exp_offset) - 1 + if e_min - (a_exp + b_exp) == -1: + b_exp -= 1 + elif e_min - (a_exp + b_exp) == 1: + b_exp += 1 + + a_fp = generate_FP(e_bits, str(a_sign), a_exp, a_bin, e_bias) + b_fp = generate_FP(e_bits, str(b_sign), b_exp, b_bin, e_bias) + + if genTests: + run_and_store_test_vector( + f"{OP_MUL}_{rounding_mode}_{a_fp}_{b_fp}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f + ) + + return (a_fp, b_fp) + + def mul_div_grs_gen( operation: str, precision: str, @@ -489,7 +636,7 @@ def mul_div_grs_gen( target_exp = first_bit if grs_int == 1: smallest_res_exp = min_exp - (2 * m_bits) - target_exp = random.randint(smallest_res_exp, g_exp) + target_exp = random.randint(smallest_res_exp, target_exp) if operation == OP_MUL: a_exp, b_exp = genSpecExp_mul(precision, target_exp, hashString + grs + sign) @@ -592,13 +739,18 @@ def tests_multiply_3_4(precision: str, rounding_mode: str, test_f: TextIO, cover def tests_multiply_5_6(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: min_exp = UNBIASED_EXP[precision][0] - # MinNorm - 3ulp - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "100", min_exp, "0", test_f, cover_f, "minExp", True - ) # Positive Test - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "100", min_exp, "1", test_f, cover_f, "minExp", True - ) # Negative Test + + # MinNorm - 3 ulp + factor_mul_gen(precision, rounding_mode, test_f, cover_f, "101", "0", True) # Positive Test + factor_mul_gen(precision, rounding_mode, test_f, cover_f, "101", "1", True) # Negative Test + + # MinNorm - 2 ulp + factor_mul_gen(precision, rounding_mode, test_f, cover_f, "110", "0", True) # Positive Test + factor_mul_gen(precision, rounding_mode, test_f, cover_f, "110", "1", True) # Negative Test + + # MinNorm - 1 ulp + factor_mul_gen(precision, rounding_mode, test_f, cover_f, "111", "0", True) # Positive Test + factor_mul_gen(precision, rounding_mode, test_f, cover_f, "111", "1", True) # Negative Test # MinNorm mul_div_grs_gen( @@ -608,6 +760,22 @@ def tests_multiply_5_6(precision: str, rounding_mode: str, test_f: TextIO, cover OP_MUL, precision, rounding_mode, "100", min_exp, "1", test_f, cover_f, "minExp", True ) # Negative Test + # MinNorm + 1 ulp + factor_mul_gen(precision, rounding_mode, test_f, cover_f, "001", "0", True) # Positive Test + factor_mul_gen(precision, rounding_mode, test_f, cover_f, "001", "1", True) # Negative Test + + # MinNorm + 2 ulp + factor_mul_gen(precision, rounding_mode, test_f, cover_f, "010", "0", True) # Positive Test + factor_mul_gen( + precision, rounding_mode, test_f, cover_f, "010", "1", True + ) # Negative Test #BF_16 is producing an error + + # #MinNorm + 3 ulp + factor_mul_gen(precision, rounding_mode, test_f, cover_f, "011", "0", True) # Positive Test + factor_mul_gen( + precision, rounding_mode, test_f, cover_f, "011", "1", True + ) # Negative Test #FP_128 is producing an error + def tests_multiply_7_8(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: m_bits = MANTISSA_BITS[precision] @@ -621,7 +789,6 @@ def tests_multiply_7_8(precision: str, rounding_mode: str, test_f: TextIO, cover def tests_multiply_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: min_exp = UNBIASED_EXP[precision][0] - e_bias = EXPONENT_BIAS[precision] # all values from minNorm.exp to minNorm.exp + 5 min_exp_range = min_exp + 1 # minNorm.exp + 1, we want it randomized, so add 1 and lsb = 0, r = 1, s = 1 @@ -630,8 +797,6 @@ def tests_multiply_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f for target_exp in range(min_exp_range, max_exp_range + 1): # Because end is exclusive seed("b5" + OP_MUL + precision + rounding_mode + str(target_exp)) sign = str(random.randint(0, 1)) - # print("Unbiased Target_exp:", target_exp) - print("Biased Target_exp:", target_exp + e_bias) mul_div_grs_gen( OP_MUL, precision, rounding_mode, "011", target_exp, sign, test_f, cover_f, str(target_exp), True @@ -640,13 +805,13 @@ def tests_multiply_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f def multiplyTests(test_f: TextIO, cover_f: TextIO) -> None: for precision in FLOAT_FMTS: - # precision = FMT_SINGLE #TODO: Alter to have all precisions + # precision = FMT_BF16 #TODO: Alter to have all precisions for rounding_mode in ROUNDING_MODES: - tests_multiply_1_2(precision, rounding_mode, test_f, cover_f) - tests_multiply_3_4(precision, rounding_mode, test_f, cover_f) + # tests_multiply_1_2(precision, rounding_mode, test_f, cover_f) + # tests_multiply_3_4(precision, rounding_mode, test_f, cover_f) tests_multiply_5_6(precision, rounding_mode, test_f, cover_f) - tests_multiply_7_8(precision, rounding_mode, test_f, cover_f) - tests_multiply_9(precision, rounding_mode, test_f, cover_f) + # tests_multiply_7_8(precision, rounding_mode, test_f, cover_f) + # tests_multiply_9(precision, rounding_mode, test_f, cover_f) def fma_gen( @@ -798,37 +963,37 @@ def tests_fma_5_6(operation: str, precision: str, rounding_mode: str, test_f: Te # MinN - 3 ulp; G = 1, R = 0, S = 0 fma_gen( - operation, precision, rounding_mode, 0, "100", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-3_ulp_5" + operation, precision, rounding_mode, 0, "100", minSNPos + 1, 0, "1*m-1_0", test_f, cover_f, "pos_-3_ulp_5" ) # Get rid of G Bit and some more from sticky, result is negative - fma_gen(operation, precision, rounding_mode, 1, "100", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-3_ulp_6") + fma_gen(operation, precision, rounding_mode, 1, "100", minSNPos + 1, 1, "1*m-1_0", test_f, cover_f, "neg_-3_ulp_6") - # MinN - 2 ulp; G = 1, R = 0, S = 1 - fma_gen( - operation, precision, rounding_mode, 0, "101", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-2_ulp_5" - ) # Get rid of G Bit and some more from sticky, result is negative - fma_gen(operation, precision, rounding_mode, 1, "101", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-2_ulp_6") + # # MinN - 2 ulp; G = 1, R = 0, S = 1 + # fma_gen( + # operation, precision, rounding_mode, 0, "101", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-2_ulp_5" + # ) # Get rid of G Bit and some more from sticky, result is negative + # fma_gen(operation, precision, rounding_mode, 1, "101", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-2_ulp_6") - # MinN - 1 ulp; G = 1, R = 1, S = 1 - fma_gen( - operation, precision, rounding_mode, 0, "110", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-2_ulp_5" - ) # Get rid of G Bit and some more from sticky, result is negative - fma_gen(operation, precision, rounding_mode, 1, "110", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-2_ulp_6") + # # MinN - 1 ulp; G = 1, R = 1, S = 1 + # fma_gen( + # operation, precision, rounding_mode, 0, "110", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-2_ulp_5" + # ) # Get rid of G Bit and some more from sticky, result is negative + # fma_gen(operation, precision, rounding_mode, 1, "110", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-2_ulp_6") - # MinN - fma_gen(operation, precision, rounding_mode, 1, "100", minSNPos, 0, "1_0*m-1_1", test_f, cover_f, "pos_norm_5") - fma_gen(operation, precision, rounding_mode, 0, "100", minSNPos, 1, "1_0*m-1_1", test_f, cover_f, "neg_norm_6") + # # MinN + # fma_gen(operation, precision, rounding_mode, 1, "100", minSNPos, 0, "1_0*m-1_1", test_f, cover_f, "pos_norm_5") + # fma_gen(operation, precision, rounding_mode, 0, "100", minSNPos, 1, "1_0*m-1_1", test_f, cover_f, "neg_norm_6") - # MinN + 1 - fma_gen(operation, precision, rounding_mode, 0, "001", minSNPos, 0, "min_n", test_f, cover_f, "pos_+1_ulp_5") - fma_gen(operation, precision, rounding_mode, 1, "001", minSNPos, 1, "min_n", test_f, cover_f, "neg_+1_ulp_6") + # # MinN + 1 ulp + # fma_gen(operation, precision, rounding_mode, 0, "001", minSNPos, 0, "min_n", test_f, cover_f, "pos_+1_ulp_5") + # fma_gen(operation, precision, rounding_mode, 1, "001", minSNPos, 1, "min_n", test_f, cover_f, "neg_+1_ulp_6") - # MinN + 2 - fma_gen(operation, precision, rounding_mode, 0, "010", minSNPos, 0, "min_n", test_f, cover_f, "pos_+2_ulp_5") - fma_gen(operation, precision, rounding_mode, 1, "010", minSNPos, 1, "min_n", test_f, cover_f, "neg_+2_ulp_6") + # # MinN + 2 ulp + # fma_gen(operation, precision, rounding_mode, 0, "010", minSNPos, 0, "min_n", test_f, cover_f, "pos_+2_ulp_5") + # fma_gen(operation, precision, rounding_mode, 1, "010", minSNPos, 1, "min_n", test_f, cover_f, "neg_+2_ulp_6") - # MinN + 3 - fma_gen(operation, precision, rounding_mode, 0, "011", minSNPos, 0, "min_n", test_f, cover_f, "pos_+3_ulp_5") - fma_gen(operation, precision, rounding_mode, 1, "011", minSNPos, 1, "min_n", test_f, cover_f, "neg_+3_ulp_6") + # # MinN + 3 ulp + # fma_gen(operation, precision, rounding_mode, 0, "011", minSNPos, 0, "min_n", test_f, cover_f, "pos_+3_ulp_5") + # fma_gen(operation, precision, rounding_mode, 1, "011", minSNPos, 1, "min_n", test_f, cover_f, "neg_+3_ulp_6") def tests_fma_7_8(operation: str, precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: @@ -883,11 +1048,11 @@ def fmaTests(test_f: TextIO, cover_f: TextIO) -> None: # precision = FMT_HALF # operation = OP_FMADD for rounding_mode in ROUNDING_MODES: - tests_fma_1_2(operation, precision, rounding_mode, test_f, cover_f) - tests_fma_3_4(operation, precision, rounding_mode, test_f, cover_f) + # tests_fma_1_2(operation, precision, rounding_mode, test_f, cover_f) + # tests_fma_3_4(operation, precision, rounding_mode, test_f, cover_f) tests_fma_5_6(operation, precision, rounding_mode, test_f, cover_f) - tests_fma_7_8(operation, precision, rounding_mode, test_f, cover_f) - tests_fma_9(operation, precision, rounding_mode, test_f, cover_f) + # tests_fma_7_8(operation, precision, rounding_mode, test_f, cover_f) + # tests_fma_9(operation, precision, rounding_mode, test_f, cover_f) def div_grs_mant( @@ -1204,11 +1369,14 @@ def main() -> None: Path("./tests/testvectors/B5_tv.txt").open("w") as test_f, Path("./tests/covervectors/B5_cv.txt").open("w") as cover_f, ): - convertTests(test_f, cover_f) - multiplyTests(test_f, cover_f) - addSubTests(test_f, cover_f) + # convertTests(test_f, cover_f) + # multiplyTests(test_f, cover_f) + # addSubTests(test_f, cover_f) fmaTests(test_f, cover_f) - divTests(test_f, cover_f) + # divTests(test_f, cover_f) + # input_filename = "./tests/covervectors/B5_cv.txt" + # output_filename = "./tests/readable/decoded_results.txt" + # softfloat_parser.parse_test_vectors(TestRange=(1, 50), file=input_filename, output_file=output_filename) if __name__ == "__main__": From 6a0254f52983b39178055459081dc2f652e2a2a4 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Mon, 27 Apr 2026 19:05:31 -0700 Subject: [PATCH 18/21] improved FMA Tests --- src/cover_float/testgen/B5.py | 176 +++++++++++++++++----------------- 1 file changed, 89 insertions(+), 87 deletions(-) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 2f5ad55..527fe6b 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -36,6 +36,8 @@ from cover_float.common.util import reproducible_hash from cover_float.reference import run_and_store_test_vector +# from cover_float.testgen import softfloat_parser + B5_FMTS = [FMT_QUAD, FMT_DOUBLE, FMT_SINGLE, FMT_BF16, FMT_HALF] ROUNDING_MODES = [ROUND_NEAR_EVEN, ROUND_MINMAG, ROUND_MIN, ROUND_MAX, ROUND_NEAR_MAXMAG] FMA_OPS = [OP_FMADD, OP_FMSUB, OP_FNMADD, OP_FNMSUB] @@ -633,6 +635,8 @@ def mul_div_grs_gen( grs_int = int(grs, 2) + print("GRS_INT:", grs_int) + target_exp = first_bit if grs_int == 1: smallest_res_exp = min_exp - (2 * m_bits) @@ -770,7 +774,7 @@ def tests_multiply_5_6(precision: str, rounding_mode: str, test_f: TextIO, cover precision, rounding_mode, test_f, cover_f, "010", "1", True ) # Negative Test #BF_16 is producing an error - # #MinNorm + 3 ulp + # MinNorm + 3 ulp factor_mul_gen(precision, rounding_mode, test_f, cover_f, "011", "0", True) # Positive Test factor_mul_gen( precision, rounding_mode, test_f, cover_f, "011", "1", True @@ -805,7 +809,7 @@ def tests_multiply_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f def multiplyTests(test_f: TextIO, cover_f: TextIO) -> None: for precision in FLOAT_FMTS: - # precision = FMT_BF16 #TODO: Alter to have all precisions + # precision = FMT_SINGLE #TODO: Alter to have all precisions for rounding_mode in ROUNDING_MODES: # tests_multiply_1_2(precision, rounding_mode, test_f, cover_f) # tests_multiply_3_4(precision, rounding_mode, test_f, cover_f) @@ -835,8 +839,8 @@ def fma_gen( fma_op_key = { OP_FMADD: {"mul_sign": 0, "add_sign": 0}, OP_FMSUB: {"mul_sign": 0, "add_sign": 1}, - OP_FNMADD: {"mul_sign": 1, "add_sign": 0}, - OP_FNMSUB: {"mul_sign": 1, "add_sign": 1}, + OP_FNMADD: {"mul_sign": 1, "add_sign": 1}, + OP_FNMSUB: {"mul_sign": 1, "add_sign": 0}, } # Determine the desired output signs for multiplication and addition @@ -922,13 +926,13 @@ def tests_fma_3_4(operation: str, precision: str, rounding_mode: str, test_f: Te minSNPos = min_exp - m_bits - # MinSN - 3 ulp; G = 0, R = 0, S = 1 + # MinSN - 3 ulp; G = 1, R = 0, S = 1 fma_gen( operation, precision, rounding_mode, 0, "101", minSNPos, 1, "min_sn", test_f, cover_f, "pos_-3_ulp_3" ) # Get rid of G Bit and some more from sticky, result is negative fma_gen(operation, precision, rounding_mode, 1, "101", minSNPos, 0, "min_sn", test_f, cover_f, "neg_-3_ulp_4") - # MinSN - 2 ulp; G = 0, R = 1, S = 0 + # MinSN - 2 ulp; G = 1, R = 1, S = 0 fma_gen( operation, precision, rounding_mode, 1, "010", minSNPos, 0, "min_sn", test_f, cover_f, "pos_-2_ulp_3" ) # Subtract minSN from 1 bit 2*minSN @@ -961,39 +965,39 @@ def tests_fma_5_6(operation: str, precision: str, rounding_mode: str, test_f: Te minSNPos = min_exp - m_bits - # MinN - 3 ulp; G = 1, R = 0, S = 0 + # MinN - 3 ulp; G = 1, R = 0, S = 1 fma_gen( - operation, precision, rounding_mode, 0, "100", minSNPos + 1, 0, "1*m-1_0", test_f, cover_f, "pos_-3_ulp_5" + operation, precision, rounding_mode, 0, "101", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-3_ulp_5" ) # Get rid of G Bit and some more from sticky, result is negative - fma_gen(operation, precision, rounding_mode, 1, "100", minSNPos + 1, 1, "1*m-1_0", test_f, cover_f, "neg_-3_ulp_6") + fma_gen(operation, precision, rounding_mode, 1, "101", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-3_ulp_6") - # # MinN - 2 ulp; G = 1, R = 0, S = 1 - # fma_gen( - # operation, precision, rounding_mode, 0, "101", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-2_ulp_5" - # ) # Get rid of G Bit and some more from sticky, result is negative - # fma_gen(operation, precision, rounding_mode, 1, "101", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-2_ulp_6") + # MinN - 2 ulp; G = 1, R = 1, S = 0 + fma_gen( + operation, precision, rounding_mode, 0, "110", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-2_ulp_5" + ) # Get rid of G Bit and some more from sticky, result is negative + fma_gen(operation, precision, rounding_mode, 1, "110", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-2_ulp_6") - # # MinN - 1 ulp; G = 1, R = 1, S = 1 - # fma_gen( - # operation, precision, rounding_mode, 0, "110", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-2_ulp_5" - # ) # Get rid of G Bit and some more from sticky, result is negative - # fma_gen(operation, precision, rounding_mode, 1, "110", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-2_ulp_6") + # MinN - 1 ulp; G = 1, R = 1, S = 1 + fma_gen( + operation, precision, rounding_mode, 0, "111", minSNPos, 0, "1*m-1_0", test_f, cover_f, "pos_-2_ulp_5" + ) # Get rid of G Bit and some more from sticky, result is negative + fma_gen(operation, precision, rounding_mode, 1, "111", minSNPos, 1, "1*m-1_0", test_f, cover_f, "neg_-2_ulp_6") - # # MinN - # fma_gen(operation, precision, rounding_mode, 1, "100", minSNPos, 0, "1_0*m-1_1", test_f, cover_f, "pos_norm_5") - # fma_gen(operation, precision, rounding_mode, 0, "100", minSNPos, 1, "1_0*m-1_1", test_f, cover_f, "neg_norm_6") + # MinN + fma_gen(operation, precision, rounding_mode, 1, "100", minSNPos, 0, "1_0*m-1_1", test_f, cover_f, "pos_norm_5") + fma_gen(operation, precision, rounding_mode, 0, "100", minSNPos, 1, "1_0*m-1_1", test_f, cover_f, "neg_norm_6") - # # MinN + 1 ulp - # fma_gen(operation, precision, rounding_mode, 0, "001", minSNPos, 0, "min_n", test_f, cover_f, "pos_+1_ulp_5") - # fma_gen(operation, precision, rounding_mode, 1, "001", minSNPos, 1, "min_n", test_f, cover_f, "neg_+1_ulp_6") + # MinN + 1 ulp + fma_gen(operation, precision, rounding_mode, 0, "001", minSNPos, 0, "min_n", test_f, cover_f, "pos_+1_ulp_5") + fma_gen(operation, precision, rounding_mode, 1, "001", minSNPos, 1, "min_n", test_f, cover_f, "neg_+1_ulp_6") - # # MinN + 2 ulp - # fma_gen(operation, precision, rounding_mode, 0, "010", minSNPos, 0, "min_n", test_f, cover_f, "pos_+2_ulp_5") - # fma_gen(operation, precision, rounding_mode, 1, "010", minSNPos, 1, "min_n", test_f, cover_f, "neg_+2_ulp_6") + # MinN + 2 ulp + fma_gen(operation, precision, rounding_mode, 0, "010", minSNPos, 0, "min_n", test_f, cover_f, "pos_+2_ulp_5") + fma_gen(operation, precision, rounding_mode, 1, "010", minSNPos, 1, "min_n", test_f, cover_f, "neg_+2_ulp_6") - # # MinN + 3 ulp - # fma_gen(operation, precision, rounding_mode, 0, "011", minSNPos, 0, "min_n", test_f, cover_f, "pos_+3_ulp_5") - # fma_gen(operation, precision, rounding_mode, 1, "011", minSNPos, 1, "min_n", test_f, cover_f, "neg_+3_ulp_6") + # MinN + 3 ulp + fma_gen(operation, precision, rounding_mode, 0, "011", minSNPos, 0, "min_n", test_f, cover_f, "pos_+3_ulp_5") + fma_gen(operation, precision, rounding_mode, 1, "011", minSNPos, 1, "min_n", test_f, cover_f, "neg_+3_ulp_6") def tests_fma_7_8(operation: str, precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: @@ -1045,14 +1049,12 @@ def tests_fma_9(operation: str, precision: str, rounding_mode: str, test_f: Text def fmaTests(test_f: TextIO, cover_f: TextIO) -> None: for operation in FMA_OPS: for precision in FLOAT_FMTS: - # precision = FMT_HALF - # operation = OP_FMADD for rounding_mode in ROUNDING_MODES: - # tests_fma_1_2(operation, precision, rounding_mode, test_f, cover_f) - # tests_fma_3_4(operation, precision, rounding_mode, test_f, cover_f) + tests_fma_1_2(operation, precision, rounding_mode, test_f, cover_f) + tests_fma_3_4(operation, precision, rounding_mode, test_f, cover_f) tests_fma_5_6(operation, precision, rounding_mode, test_f, cover_f) - # tests_fma_7_8(operation, precision, rounding_mode, test_f, cover_f) - # tests_fma_9(operation, precision, rounding_mode, test_f, cover_f) + tests_fma_7_8(operation, precision, rounding_mode, test_f, cover_f) + tests_fma_9(operation, precision, rounding_mode, test_f, cover_f) def div_grs_mant( @@ -1135,53 +1137,53 @@ def tests_div_3_4(precision: str, rounding_mode: str, test_f: TextIO, cover_f: T OP_DIV, precision, rounding_mode, "001", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True ) # Negative Test - # minSN - 2 ulp G = 0, R = 1, S = 0 - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "010", minSNPos, "0", test_f, cover_f, "minSN-2ulppos", True - ) # Positive Test - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "010", minSNPos, "1", test_f, cover_f, "minSN-2ulpneg", True - ) # Negative Test - - # minSN - 1 ulp G = 0, R = 1, S = 1 - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "011", minSNPos, "0", test_f, cover_f, "minSN-1ulppos", True - ) # Positive Test - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "011", minSNPos, "1", test_f, cover_f, "minSN-1ulpneg", True - ) # Negative Test - - # minSN G = 1, R = 0, S = 0 - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "100", minSNPos, "0", test_f, cover_f, "minSNpos", True - ) # Positive Test - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "100", minSNPos, "1", test_f, cover_f, "minSNneg", True - ) # Negative Test - - # minSN + 1 ulp G = 1, R = 0, S = 1 - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "101", minSNPos, "0", test_f, cover_f, "minSN+1ulppos", True - ) # Positive Test - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "101", minSNPos, "1", test_f, cover_f, "minSN+1ulpneg", True - ) # Negative Test - - # minSN + 2 ulp G = 1, R = 1, S = 0 - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "110", minSNPos, "0", test_f, cover_f, "minSN+2ulppos", True - ) # Positive Test - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "110", minSNPos, "1", test_f, cover_f, "minSN+2ulpneg", True - ) # Negative Test - - # minSN + 3 ulp G = 1, R = 1, S = 1 - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "111", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True - ) # Positive Test - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "111", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True - ) # Negative Test + # # minSN - 2 ulp G = 0, R = 1, S = 0 + # mul_div_grs_gen( + # OP_DIV, precision, rounding_mode, "010", minSNPos, "0", test_f, cover_f, "minSN-2ulppos", True + # ) # Positive Test + # mul_div_grs_gen( + # OP_DIV, precision, rounding_mode, "010", minSNPos, "1", test_f, cover_f, "minSN-2ulpneg", True + # ) # Negative Test + + # # minSN - 1 ulp G = 0, R = 1, S = 1 + # mul_div_grs_gen( + # OP_DIV, precision, rounding_mode, "011", minSNPos, "0", test_f, cover_f, "minSN-1ulppos", True + # ) # Positive Test + # mul_div_grs_gen( + # OP_DIV, precision, rounding_mode, "011", minSNPos, "1", test_f, cover_f, "minSN-1ulpneg", True + # ) # Negative Test + + # # minSN G = 1, R = 0, S = 0 + # mul_div_grs_gen( + # OP_DIV, precision, rounding_mode, "100", minSNPos, "0", test_f, cover_f, "minSNpos", True + # ) # Positive Test + # mul_div_grs_gen( + # OP_DIV, precision, rounding_mode, "100", minSNPos, "1", test_f, cover_f, "minSNneg", True + # ) # Negative Test + + # # minSN + 1 ulp G = 1, R = 0, S = 1 + # mul_div_grs_gen( + # OP_DIV, precision, rounding_mode, "101", minSNPos, "0", test_f, cover_f, "minSN+1ulppos", True + # ) # Positive Test + # mul_div_grs_gen( + # OP_DIV, precision, rounding_mode, "101", minSNPos, "1", test_f, cover_f, "minSN+1ulpneg", True + # ) # Negative Test + + # # minSN + 2 ulp G = 1, R = 1, S = 0 + # mul_div_grs_gen( + # OP_DIV, precision, rounding_mode, "110", minSNPos, "0", test_f, cover_f, "minSN+2ulppos", True + # ) # Positive Test + # mul_div_grs_gen( + # OP_DIV, precision, rounding_mode, "110", minSNPos, "1", test_f, cover_f, "minSN+2ulpneg", True + # ) # Negative Test + + # # minSN + 3 ulp G = 1, R = 1, S = 1 + # mul_div_grs_gen( + # OP_DIV, precision, rounding_mode, "111", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True + # ) # Positive Test + # mul_div_grs_gen( + # OP_DIV, precision, rounding_mode, "111", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True + # ) # Negative Test def tests_div_7_8(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: @@ -1211,7 +1213,7 @@ def tests_div_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f: Tex def divTests(test_f: TextIO, cover_f: TextIO) -> None: # for precision in FLOAT_FMTS: - precision = FMT_HALF + precision = FMT_BF16 for rounding_mode in ROUNDING_MODES: # tests_div_1_2(precision, rounding_mode, test_f, cover_f) tests_div_3_4(precision, rounding_mode, test_f, cover_f) @@ -1372,8 +1374,8 @@ def main() -> None: # convertTests(test_f, cover_f) # multiplyTests(test_f, cover_f) # addSubTests(test_f, cover_f) - fmaTests(test_f, cover_f) - # divTests(test_f, cover_f) + # fmaTests(test_f, cover_f) + divTests(test_f, cover_f) # input_filename = "./tests/covervectors/B5_cv.txt" # output_filename = "./tests/readable/decoded_results.txt" # softfloat_parser.parse_test_vectors(TestRange=(1, 50), file=input_filename, output_file=output_filename) From c8d16c657e8cc7462e8c424f1157a6461c347415 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Wed, 6 May 2026 19:58:54 -0700 Subject: [PATCH 19/21] B5/B18/B29 --- Makefile | 5 +- config.svh | 30 +- coverage/coverfloat_pkg.sv | 32 ++ coverage/covergroups/B29.svh | 141 ++++++++ src/cover_float/cli.py | 5 + src/cover_float/testgen/B18.py | 343 ++++++++++++++++++ src/cover_float/testgen/B5.py | 519 +++++++++++----------------- src/cover_float/testgen/__init__.py | 2 + 8 files changed, 749 insertions(+), 328 deletions(-) create mode 100644 coverage/covergroups/B29.svh create mode 100644 src/cover_float/testgen/B18.py diff --git a/Makefile b/Makefile index d289b6c..83c425c 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ RM_CMD ?= rm -rf -.PHONY: build clean sim all B1 B2 B3 B9 B10 B12 B13 B14 B15 B21 +.PHONY: build clean sim all B1 B2 B3 B9 B10 B12 B13 B14 B15 B18 B21 # Notice that we pass --managed-python, we do this so that uv (scikit-build-core) # will have a python enviornment with Python.h to build with. @@ -59,6 +59,9 @@ B14: B15: uv run --managed-python cover-float-testgen --model B15 +B18: + uv run --managed-python cover-float-testgen --model B18 + B20: uv run --managed-python cover-float-testgen --model B20 diff --git a/config.svh b/config.svh index f30a234..09a6af4 100644 --- a/config.svh +++ b/config.svh @@ -1,35 +1,35 @@ // define macros for which models to check coverage for // e.g. `define COVER_B1 for model B1 -`define COVER_B1 -`define COVER_B2 -`define COVER_B3 -`define COVER_B4 -`define COVER_B5 +// `define COVER_B1 +// `define COVER_B2 +// `define COVER_B3 +// `define COVER_B4 +// `define COVER_B5 // `define COVER_B6 // `define COVER_B7 // `define COVER_B8 -`define COVER_B9 -`define COVER_B10 +// `define COVER_B9 +// `define COVER_B10 // `define COVER_B11 -`define COVER_B12 +// `define COVER_B12 // `define COVER_B13 -`define COVER_B14 +// `define COVER_B14 // `define COVER_B15 -`define COVER_B16 +// `define COVER_B16 // `define COVER_B17 -// `define COVER_B18 -`define COVER_B19 +`define COVER_B18 +// `define COVER_B19 // `define COVER_B20 -`define COVER_B21 -`define COVER_B22 +// `define COVER_B21 +// `define COVER_B22 // `define COVER_B23 // `define COVER_B24 // `define COVER_B25 // `define COVER_B26 // `define COVER_B27 // `define COVER_B28 -// `define COVER_B29 +`define COVER_B29 // define macros for which precisions to check coverage for // e.g. `define COVER_F32 for single precision diff --git a/coverage/coverfloat_pkg.sv b/coverage/coverfloat_pkg.sv index f0680bd..c4a3896 100644 --- a/coverage/coverfloat_pkg.sv +++ b/coverage/coverfloat_pkg.sv @@ -43,6 +43,7 @@ package coverfloat_pkg; const logic [31:0] OP_FSGNJ = 32'h10_1; const logic [31:0] OP_FSGNJN = 32'h10_2; const logic [31:0] OP_FSGNJX = 32'h10_3; + const logic [32:0] OP_RFI = 32'h11_0; // const logic [31:0] OP_ @@ -85,6 +86,12 @@ package coverfloat_pkg; parameter int F64_M_BITS = 52; parameter int F128_M_BITS = 112; + parameter int F32_FMA_PRE_ADDITION_NF = 2 * F32_M_BITS; + parameter int F64_FMA_PRE_ADDITION_NF = 2 * F64_M_BITS; + parameter int F128_FMA_PRE_ADDITION_NF = 2 * F128_M_BITS; + parameter int F16_FMA_PRE_ADDITION_NF = 2 * F16_M_BITS; + parameter int BF16_FMA_PRE_ADDITION_NF = 2 * BF16_M_BITS; + // Precision (p = number of significand bits + 1 implicit bits) parameter int F16_P = F16_M_BITS + 1; parameter int BF16_P = BF16_M_BITS + 1; @@ -374,7 +381,32 @@ package coverfloat_pkg; longest_seq_of_ones = max_len; end endfunction + function automatic int get_effective_product_exponent( + input logic[127:0] a, + input logic[127:0] b, + input logic[255:0] pre_addition, + input logic [7:0] fmt + ); + int shift_one; + case (fmt) + FMT_BF16: begin + shift_one = pre_addition[BF16_FMA_PRE_ADDITION_NF+1]; + end + FMT_HALF: begin + shift_one = pre_addition[F16_FMA_PRE_ADDITION_NF+1]; + end + FMT_SINGLE: begin + shift_one = pre_addition[F32_FMA_PRE_ADDITION_NF+1]; + end + FMT_DOUBLE: begin + shift_one = pre_addition[F64_FMA_PRE_ADDITION_NF+1]; + end + FMT_QUAD: begin + shift_one = pre_addition[F128_FMA_PRE_ADDITION_NF+1]; + end + endcase + endfunction function automatic int get_product_exponent ( input logic [127:0] a, diff --git a/coverage/covergroups/B29.svh b/coverage/covergroups/B29.svh new file mode 100644 index 0000000..ad5ba38 --- /dev/null +++ b/coverage/covergroups/B29.svh @@ -0,0 +1,141 @@ +covergroup B29_cg (virtual coverfloat_interface CFI); + + option.per_instance = 0; + + F16_input_fmt: coverpoint (CFI.operandFmt == FMT_HALF) { + type_option.weight = 0; + bins f16 = {1}; + } + + BF16_input_fmt: coverpoint (CFI.operandFmt == FMT_BF16) { + type_option.weight = 0; + bins bf16 = {1}; + } + + F32_input_fmt: coverpoint (CFI.operandFmt == FMT_SINGLE) { + type_option.weight = 0; + bins f32 = {1}; + } + + F64_input_fmt: coverpoint (CFI.operandFmt == FMT_DOUBLE) { + type_option.weight = 0; + bins f64 = {1}; + } + + F128_input_fmt: coverpoint (CFI.operandFmt == FMT_QUAD) { + type_option.weight = 0; + bins f128 = {1}; + } + // RFI Instruction + + RFI_op: coverpoint CFI.op { + type_option.weight = 0; + bins rfi = { OP_RFI }; + } + + rounding_mode_all: coverpoint CFI.rm { + type_option.weight = 0; + bins round_near_even = {ROUND_NEAR_EVEN}; + bins round_minmag = {ROUND_MINMAG}; + bins round_min = {ROUND_MIN}; + bins round_max = {ROUND_MAX}; + bins round_near_maxmag = {ROUND_NEAR_MAXMAG}; + } + + F32_sign: coverpoint CFI.result[31] { + type_option.weight = 0; + bins pos = {0}; + bins neg = {1}; + } + + F64_sign: coverpoint CFI.result[63] { + type_option.weight = 0; + bins pos = {0}; + bins neg = {1}; + } + + F128_sign: coverpoint CFI.result[127] { + type_option.weight = 0; + bins pos = {0}; + bins neg = {1}; + } + + F16_sign: coverpoint CFI.result[15] { + type_option.weight = 0; + bins pos = {0}; + bins neg = {1}; + } + + BF16_sign: coverpoint CFI.result[15] { + type_option.weight = 0; + bins pos = {0}; + bins neg = {1}; + } + + BF16_LGS_Combos: coverpoint { + CFI.a[BF16_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_BF16)) + 1], + CFI.a[BF16_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_BF16))], + |(CFI.a & ((64'b1 << (BF16_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_BF16)))) - 1)) + } { + type_option.weight = 0; + bins lgs_combos[] = {[3'b000 : 3'b111]}; + } + + F16_LGS_Combos: coverpoint { + CFI.a[F16_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_HALF)) + 1], + CFI.a[F16_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_HALF))], + |(CFI.a & ((64'b1 << (F16_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_HALF)))) - 1)) + } { + type_option.weight = 0; + bins lgs_combos[] = {[3'b000 : 3'b111]}; + } + + F32_LGS_Combos: coverpoint { + CFI.a[F32_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_SINGLE)) + 1], + CFI.a[F32_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_SINGLE))], + |(CFI.a & ((64'b1 << (F32_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_SINGLE)))) - 1)) + } { + type_option.weight = 0; + bins lgs_combos[] = {[3'b000 : 3'b111]}; + } + + + F64_LGS_Combos: coverpoint { + CFI.a[F64_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_DOUBLE)) + 1], + CFI.a[F64_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_DOUBLE))], + |(CFI.a & ((64'b1 << (F64_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_DOUBLE)))) - 1)) + } { + type_option.weight = 0; + bins lgs_combos[] = {[3'b000 : 3'b111]}; + } + + F128_LGS_Combos: coverpoint { + CFI.a[F128_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_QUAD)) + 1], + CFI.a[F128_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_QUAD))], + |(CFI.a & ((64'b1 << (F128_M_UPPER - $signed(get_unbiased_exponent(CFI.a, FMT_QUAD)))) - 1)) + } { + type_option.weight = 0; + bins lgs_combos[] = {[3'b000 : 3'b111]}; + } + + `ifdef COVER_F32 + B5_F32_LGS: cross F32_sign, RFI_op, F32_input_fmt, F32_LGS_Combos, rounding_mode_all; + `endif + + `ifdef COVER_F64 + B5_F64_LGS: cross F64_sign, RFI_op, F64_input_fmt, F64_LGS_Combos, rounding_mode_all; + `endif + + `ifdef COVER_F128 + B5_F128_LGS: cross F128_sign, RFI_op, F128_input_fmt, F128_LGS_Combos, rounding_mode_all; + `endif + + `ifdef COVER_F16 + B5_F16_LGS: cross F16_sign, RFI_op, F16_input_fmt, F16_LGS_Combos, rounding_mode_all; + `endif + + `ifdef COVER_BF16 + B5_BF16_LGS: cross BF16_sign, RFI_op, BF16_input_fmt, BF16_LGS_Combos, rounding_mode_all; + `endif + +endgroup diff --git a/src/cover_float/cli.py b/src/cover_float/cli.py index 67de07f..e479348 100644 --- a/src/cover_float/cli.py +++ b/src/cover_float/cli.py @@ -84,6 +84,8 @@ def testgen() -> None: auto_parse("B14", args.output_dir) tg.B15.main() auto_parse("B15", args.output_dir) + tg.B18.main() + auto_parse("B18", args.output_dir) tg.B20.main() auto_parse("B20", args.output_dir) tg.B21.main() @@ -139,6 +141,9 @@ def testgen() -> None: if "B15" in args.models: tg.B15.main() auto_parse("B15", args.output_dir) + if "B18" in args.models: + tg.B18.main() + auto_parse("B18", args.output_dir) if "B20" in args.models: tg.B20.main() auto_parse("B20", args.output_dir) diff --git a/src/cover_float/testgen/B18.py b/src/cover_float/testgen/B18.py new file mode 100644 index 0000000..32baa98 --- /dev/null +++ b/src/cover_float/testgen/B18.py @@ -0,0 +1,343 @@ +# B18 +# Lamarr + +import random +from pathlib import Path +from typing import TextIO + +from cover_float.common.constants import ( + EXPONENT_BIAS, + EXPONENT_BITS, + FLOAT_FMTS, + FMT_BF16, + FMT_DOUBLE, + FMT_HALF, + FMT_QUAD, + FMT_SINGLE, + MANTISSA_BITS, + OP_FMADD, + OP_FMSUB, + OP_FNMADD, + OP_FNMSUB, + ROUND_MAX, + ROUND_MIN, + ROUND_MINMAG, + ROUND_NEAR_EVEN, + ROUND_NEAR_MAXMAG, + UNBIASED_EXP, +) +from cover_float.reference import run_and_store_test_vector +from cover_float.testgen.B5 import getMultiplyTests + +B5_FMTS = [FMT_QUAD, FMT_DOUBLE, FMT_SINGLE, FMT_BF16, FMT_HALF] +ROUNDING_MODES = [ROUND_NEAR_EVEN, ROUND_MINMAG, ROUND_MIN, ROUND_MAX, ROUND_NEAR_MAXMAG] +FMA_OPS = [OP_FMADD, OP_FMSUB, OP_FNMADD, OP_FNMSUB] + +fma_op_key = { + OP_FMADD: {"mul_sign": 0, "add_sign": 0}, + OP_FMSUB: {"mul_sign": 0, "add_sign": 1}, + OP_FNMADD: {"mul_sign": 1, "add_sign": 1}, + OP_FNMSUB: {"mul_sign": 1, "add_sign": 0}, +} + + +def generate_FP(precision: str, input_sign: str, input_exponent: int, input_mantissa: str) -> str: + input_e_bitwidth = EXPONENT_BITS[precision] + input_bias = EXPONENT_BIAS[precision] + + # 1. Calculate and format the exponent + exp_val = input_exponent + input_bias + exponent = f"{exp_val:0{input_e_bitwidth}b}" + + # 2. Check for Exponent Overflow/Underflow + if len(exponent) != input_e_bitwidth: + raise ValueError( + f"Alignment Error: Exponent binary '{exponent}' is {len(exponent)} bits long. " + f"Expected exactly {input_e_bitwidth} bits. (Calculated value was {exp_val})" + ) + + # 3. Validate Sign Bit + if len(input_sign) != 1 or input_sign not in ("0", "1"): + raise ValueError(f"Alignment Error: Sign bit must be exactly '0' or '1'. Got: '{input_sign}'") + + # 4. Construct the full binary string + complete = input_sign + exponent + input_mantissa + total_bits = len(complete) + + # 5. Validate total bit length is a clean multiple of 4 (for hex conversion) + if total_bits % 4 != 0: + raise ValueError( + f"Alignment Error: Total bit length ({total_bits}) is not a multiple of 4. " + f"Sign: 1, Exp: {input_e_bitwidth}, Mantissa: {len(input_mantissa)}" + ) + + # 6. Convert to Hex AND explicitly pad to the correct number of characters + hex_chars_needed = total_bits // 4 + fp_complete = format(int(complete, 2), "X").zfill(hex_chars_needed) + + return fp_complete + + +def getRandomInt(min_exp: int, max_exp: int, sign: str, precision: str) -> str: + mantissa_bits = MANTISSA_BITS[precision] + + exp = random.randint(min_exp, max_exp + 1) + mantissa = random.randint(1, (1 << mantissa_bits) - 1) + mantissa_str = f"{mantissa:0{mantissa_bits}b}" + return generate_FP(precision, sign, exp, mantissa_str) + + +def genUnderflowTests(test_f: TextIO, cover_f: TextIO) -> None: + + for precision in FLOAT_FMTS: + min_exp = UNBIASED_EXP[precision][0] + 1 + max_exp = UNBIASED_EXP[precision][1] - 1 + for rounding_mode in ROUNDING_MODES: + for a, b in getMultiplyTests(precision, rounding_mode): + for operation in FMA_OPS: + c = getRandomInt(min_exp, max_exp, str(random.randint(0, 1)), precision) + run_and_store_test_vector( + f"{operation}_{rounding_mode}_{a}_{b}_{c}_{precision}_{32 * '0'}_{precision}_00", + test_f, + cover_f, + ) + + +# def genOverflowTests(test_f: TextIO, cover_f: TextIO) -> None: + + +def get_fma_signs(operation: str) -> tuple[int, int]: + fma_op_key = { + OP_FMADD: {"mul_sign": 0, "add_sign": 0}, + OP_FMSUB: {"mul_sign": 0, "add_sign": 1}, + OP_FNMADD: {"mul_sign": 1, "add_sign": 1}, + OP_FNMSUB: {"mul_sign": 1, "add_sign": 0}, + } + + op_mul_sign = fma_op_key[operation]["mul_sign"] + op_add_sign = fma_op_key[operation]["add_sign"] + + return op_mul_sign, op_add_sign + + +def lsbGuardStickyTests(test_f: TextIO, cover_f: TextIO) -> None: + rounding_mode = random.choice(ROUNDING_MODES) + for precision in FLOAT_FMTS: + for grs_int in range(1, 8): + for operation in FMA_OPS: + mul_sign, add_sign = get_fma_signs(operation) + a, b, c = get_fp_values(precision, f"{grs_int:03b}", mul_sign, add_sign) + run_and_store_test_vector( + f"{operation}_{rounding_mode}_{a}_{b}_{c}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f + ) + + +def get_fp_values(precision: str, grs_pattern: str, mul_sign: int, addend_sign: int) -> tuple[str, str, str]: + m_bits = MANTISSA_BITS[precision] + e_min = UNBIASED_EXP[precision][0] + e_max = UNBIASED_EXP[precision][1] + + # Determine the input mantissas for each desired value + target_list = { + # Above MinNorm + "001": { + FMT_BF16: (8 + 1) * (8**2 - 8 + 1), + FMT_HALF: ((2**4) + 1) * ((2**4) ** 2 - (2**4) + 1), + FMT_SINGLE: ((2**5) + 1) * ((2**5) ** 4 - (2**5) ** 3 + (2**5) ** 2 - (2**5) + 1), + FMT_DOUBLE: ((2**11) + 1) * ((2**11) ** 4 - (2**11) ** 3 + (2**11) ** 2 - (2**11) + 1), + FMT_QUAD: ((2**38) + 1) * ((2**76) - (2**38) + 1), + }, + "010": { + FMT_BF16: 2056, + FMT_HALF: (2**11) + 1, + FMT_SINGLE: ((2**8) + 1) * ((2**8) ** 2 - (2**8) + 1), + FMT_DOUBLE: (2**53) + 1, + FMT_QUAD: (2**113) + 1, + }, + "011": { + FMT_BF16: 2**9 + 2 + 1, + FMT_HALF: 2**13 + 4 + 3, + FMT_SINGLE: (2**26) + 7, + FMT_DOUBLE: 2**55 + 7, + FMT_QUAD: 2**120 + 125, + }, + "100": { # grs_int = 4 + FMT_BF16: (2**8) - 1, + FMT_HALF: (2**11) - 1, + FMT_SINGLE: (2**24) - 1, + FMT_DOUBLE: (2**53) - 1, + FMT_QUAD: (2**113) - 1, + }, + # Below MinNorm + "111": { # grs_int = 7 + FMT_BF16: (2**10) - 1, + FMT_HALF: (2**14) - 1, + FMT_SINGLE: (2**26) - 1, + FMT_DOUBLE: (2**56) - 1, + FMT_QUAD: (2**116) - 1, + }, # - 1 ulp + "110": { # grs_int = 6 + FMT_BF16: (2**9) - 1, + FMT_HALF: (2**12) - 1, + FMT_SINGLE: (2**25) - 1, + FMT_DOUBLE: (2**54) - 1, + FMT_QUAD: (2**114) - 1, + }, + "101": { # grs_int = 5 + FMT_BF16: 517, + FMT_HALF: (2**12) + 5, + FMT_SINGLE: (2**26) - 3, + FMT_DOUBLE: (2**55) - 3, + FMT_QUAD: 613 * 33881219305284356466756909162937, # FIX + }, + } + + factor_list = { + # Above MinNorm + "001": { + FMT_BF16: (8 + 1), + FMT_HALF: ((2**4) + 1), + FMT_SINGLE: ((2**5) + 1), + FMT_DOUBLE: ((2**11) + 1), + FMT_QUAD: ((2**38) + 1), + }, + "010": {FMT_BF16: 257, FMT_HALF: 683, FMT_SINGLE: ((2**8) + 1), FMT_DOUBLE: 321, FMT_QUAD: 491003369344660409}, + "011": {FMT_BF16: 103, FMT_HALF: 9, FMT_SINGLE: 23 * 29, FMT_DOUBLE: 9 * 25, FMT_QUAD: 1099511627781}, + "100": { + FMT_BF16: (2**4) - 1, + FMT_HALF: 23, + FMT_SINGLE: (2**12) - 1, + FMT_DOUBLE: 6361 * 69431, + FMT_QUAD: 3391 * 23279 * 65993, + }, + # Below MinNorm + "111": { # grs_int = 7 + FMT_BF16: (2**5) - 1, + FMT_HALF: (2**7) - 1, + FMT_SINGLE: (2**13) - 1, + FMT_DOUBLE: (2**28) - 1, + FMT_QUAD: (2**58) - 1, + }, + "110": { # grs_int = 6 + FMT_BF16: 73, + FMT_HALF: (2**4) - 1, + FMT_SINGLE: (2**5) - 1, + FMT_DOUBLE: (2**27) - 1, + FMT_QUAD: (2**57) - 1, + }, + "101": { # grs_int = 5 + FMT_BF16: 11, + FMT_HALF: 1367, + FMT_SINGLE: 37 * 349, + FMT_DOUBLE: 181 * 313 * 431, + FMT_QUAD: 33881219305284356466756909162937, # FIX + }, + } + # Randomize the sign bit + if mul_sign == 0: + a_sign = random.randint(0, 1) + b_sign = a_sign + else: + a_sign = random.randint(0, 1) + b_sign = (a_sign + 1) % 2 + + target_int = target_list[grs_pattern][precision] + factor_1 = factor_list[grs_pattern][precision] + factor_2 = target_int // factor_1 # Integers have unlimited precision, must use integer division + + a_int = int(max(factor_1, factor_2)) # Ensure a is the largest value + b_int = int(min(factor_1, factor_2)) + + a_bin = format(a_int, "b") + b_bin = format(b_int, "b") + + a_bits = len(a_bin) + b_bits = len(b_bin) + + exp_offset = a_bits - b_bits + + # Subtract Hidden 1 + a_int -= 1 << (a_bits - 1) + b_int -= 1 << (b_bits - 1) + + a_trailing_zeros = m_bits - a_bits + 1 + b_trailing_zeros = m_bits - b_bits + 1 + + a_int *= 2 ** max(a_trailing_zeros, 0) + b_int *= 2 ** max(b_trailing_zeros, 0) + + a_bin = f"{a_int:0{m_bits}b}" + b_bin = f"{b_int:0{m_bits}b}" + + e_mul_target = random.randint(e_min + m_bits + 2, e_max) + + a_exp = int((e_mul_target + exp_offset) / 2) + b_exp = int(a_exp - exp_offset) - 1 + if e_min - (a_exp + b_exp) == -1: + b_exp -= 1 + elif e_min - (a_exp + b_exp) == 1: + b_exp += 1 + target_binary = f"{target_int:b}" + target_len = len(target_binary) - 1 # subtract hidden 1 + additional_bits = max(0, target_len - m_bits) + if additional_bits > 0: # If we have bits to remove + c_len = random.randint(additional_bits, m_bits + 1) + + additional_bits_bin = target_binary[m_bits + 1 :] + additional_bits_len = len(additional_bits_bin) + c_prefix_len = c_len - additional_bits_len + c_mantissa_prefix = "" + if c_prefix_len > 0: + c_mantissa_prefix = f"{random.randint(1 << (c_prefix_len - 1), (1 << c_prefix_len) - 1):0{c_prefix_len}b}" + c_mantissa_bin = c_mantissa_prefix + additional_bits_bin + + c_mantissa_bin = c_mantissa_bin.lstrip("0") + if not c_mantissa_bin: + c_mantissa_bin = "1" # Failsafe if the slice was entirely zeros + + # Update c_len to the TRUE length after removing leading zeros + c_len = len(c_mantissa_bin) + + # Calculate precise exponent offset using the corrected c_len + c_exp_offset = a_bits + b_bits - c_len - 1 + else: # If there are no bits to remove + c_len = random.randint(1, m_bits + 1) + c_mantissa = random.randint(1 << (c_len - 1), (1 << c_len) - 1) + c_mantissa_bin = f"{c_mantissa:0{c_len}b}" + c_exp_offset = random.randint(0, m_bits - c_len) if m_bits - c_len > 0 else 0 + + mantissa_len = len(c_mantissa_bin) + c_mantissa_int = int(c_mantissa_bin, 2) + + # handle normal vs subnormals: + c_exp = (a_exp + b_exp) - c_exp_offset + if c_exp < e_min: + c_exp = e_min - 1 + else: # subtract hidden 1 + c_mantissa_int -= 1 << (mantissa_len - 1) + + # Shift the mantissa left to align it to the MSB of the IEEE fraction field + c_trailing_zeros = m_bits - c_len + 1 + c_mantissa_int *= 2 ** max(c_trailing_zeros, 0) + + c_bin = f"{c_mantissa_int:0{m_bits}b}" + + a_fp = generate_FP(precision, str(a_sign), a_exp, a_bin) + b_fp = generate_FP(precision, str(b_sign), b_exp, b_bin) + c_fp = generate_FP(precision, str((addend_sign + 1) % 2), c_exp, c_bin) + + return a_fp, b_fp, c_fp + + +def main() -> None: + with ( + Path("./tests/testvectors/B18_tv.txt").open("w") as test_f, + Path("./tests/covervectors/B18_cv.txt").open("w") as cover_f, + ): + genUnderflowTests(test_f, cover_f) + # overFlowTests(test_f, cover_f) + lsbGuardStickyTests(test_f, cover_f) + + +if __name__ == "__main__": + main() diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 527fe6b..310b4ec 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -3,6 +3,7 @@ import random +from collections.abc import Iterator from pathlib import Path from random import seed from typing import TextIO @@ -36,8 +37,6 @@ from cover_float.common.util import reproducible_hash from cover_float.reference import run_and_store_test_vector -# from cover_float.testgen import softfloat_parser - B5_FMTS = [FMT_QUAD, FMT_DOUBLE, FMT_SINGLE, FMT_BF16, FMT_HALF] ROUNDING_MODES = [ROUND_NEAR_EVEN, ROUND_MINMAG, ROUND_MIN, ROUND_MAX, ROUND_NEAR_MAXMAG] FMA_OPS = [OP_FMADD, OP_FMSUB, OP_FNMADD, OP_FNMSUB] @@ -180,7 +179,6 @@ def tests_conversion_5_6(lp: str, hp: str, rounding_mode: str, test_f: TextIO, c hashval = reproducible_hash(OP_CFF + lp + "b5") seed(hashval) hp_m_bits = MANTISSA_BITS[hp] - lp_n_exp = UNBIASED_EXP[lp][0] lp_sn_exp = UNBIASED_EXP[lp][0] - 1 hp_e_bits = EXPONENT_BITS[hp] hp_e_bias = EXPONENT_BIAS[hp] @@ -189,46 +187,53 @@ def tests_conversion_5_6(lp: str, hp: str, rounding_mode: str, test_f: TextIO, c if ( hp != FMT_BF16 and lp != FMT_SINGLE ): # The mantissa bits for bf_16 are smaller than that for single, so you can't do these operations - rem_bits = hp_m_bits + 1 - 2 - lp_m_bits + rem_bits = hp_m_bits - lp_m_bits - max_rem = int("1" * rem_bits, 2) + max_rem = (1 << rem_bits) - 1 - # MinNorm - 1 i_ulp: - hp_m_1 = "1" * (lp_m_bits - 1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_m_2 = "1" * (lp_m_bits - 1) + "1" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" + # MinNorm - 3 i_ulp: + hp_m = "1" * (lp_m_bits - 1) + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_sn_exp - genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) + genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) # MinNorm - 2 i_ulp: - hp_m = "1" * (lp_m_bits - 1) + "1" + "1" + "0" * rem_bits + hp_m = "1" * (lp_m_bits - 1) + "1" + "0" * rem_bits hp_exp = lp_sn_exp genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) - # MinNorm - 3 i_ulp: - hp_m = "1" * (lp_m_bits - 1) + "1" + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" + # MinNorm - 1 i_ulp: + hp_m_1 = "1" * (lp_m_bits - 1) + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" + hp_m_2 = "1" * (lp_m_bits - 1) + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" hp_exp = lp_sn_exp - genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) - # MinNorm + 1 i_ulp: - hp_m = "0" * (lp_m_bits - 1) + "0" + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_exp = lp_n_exp + # # MinNorm: + # hp_m_1 = "0" * (hp_m_bits) + # hp_m_2 = "0" * (hp_m_bits) + # hp_exp = lp_n_exp - genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + # genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m_1, hp_m_2, hp_e_bias, test_f, cover_f) - # MinNorm + 2 i_ulp: - hp_m = "0" * (lp_m_bits - 1) + "0" + "1" + "0" * rem_bits - hp_exp = lp_n_exp + # # MinNorm + 1 i_ulp: + # hp_m = "0" * (lp_m_bits - 1) + "0" + f"{random.randint(1, max_rem):0{rem_bits}b}" + # hp_exp = lp_n_exp - genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + # genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) - # MinNorm + 3 i_ulp: - hp_m = "0" * (lp_m_bits - 1) + "0" + "1" + f"{random.randint(1, max_rem):0{rem_bits}b}" - hp_exp = lp_n_exp + # # MinNorm + 2 i_ulp: + # hp_m = "0" * lp_m_bits + "1" + "0" * (rem_bits - 1) + # hp_exp = lp_n_exp - genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + # genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) + + # # MinNorm + 3 i_ulp: + # hp_m = ("0" * lp_m_bits) + "1" + f"{random.randint(1, (1 << (rem_bits - 1)) - 1):0{(rem_bits - 1)}b}" + # hp_exp = lp_n_exp + + # genPNTestVectors(lp, hp, rounding_mode, hp_e_bits, hp_exp, hp_m, hp_m, hp_e_bias, test_f, cover_f) def tests_conversion_7_8(lp: str, hp: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: @@ -268,8 +273,8 @@ def convertTests(test_f: TextIO, cover_f: TextIO) -> None: hp = B5_FMTS[i_hp] for i_lp in range(i_hp + 1, len(B5_FMTS)): lp = B5_FMTS[i_lp] - # hp = FMT_SINGLE - # lp = FMT_HALF + # hp = B5_FMTS[0] + # lp = B5_FMTS[4] for rounding_mode in ROUNDING_MODES: tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f) tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f) @@ -367,40 +372,25 @@ def get_grs_mant(operation: str, precision: str, a_exp: int, b_exp: int, hashStr b_mantissa += 1 << (b_bits_left - 1) elif (grs_int == 7 and operation == OP_DIV) or (grs_int == 5 and operation == OP_DIV): - a_max = (1 << (a_bits_left + 1)) - 1 - # a_min = 1 << a_bits_left # With hidden 1 - - b_min = 1 << b_bits_left - b_max = (1 << (b_bits_left + 1)) - 1 - - a_int = 0 - b_int = 0 - - if grs_int == 7 or grs_int == 5: # G = 1, R = 1, S = 1 - # b and a mantissas have the same range - b_min_range = b_min - b_max_range = (2 * b_max) // 3 - - small_int = random.randint(b_min_range, b_max_range) - - a_min_range = (3 * small_int) // 2 - a_max_range = a_max - - large_int = random.randint(a_min_range, a_max_range) - - if grs_int == 7: - a_int = large_int - b_int = small_int - else: - a_int = small_int - b_int = large_int + a_min = 1 << m_bits + a_max = 1 << m_bits + if grs_int == 7: + a_min = 7 << (m_bits - 2) + a_max = (1 << (m_bits + 1)) - 1 + a_int = random.randint(a_min, a_max) + b_int = 1 << m_bits + else: + a_min = 5 << (m_bits - 2) + a_max = (1 << m_bits) + ((1 << (m_bits - 1)) - 1) + a_int = random.randint(a_min, a_max) + b_int = random.randint((1 << m_bits), int((a_min / a_int) * a_min)) a_mantissa = a_int - (1 << a_bits_left) # Remove hidden 1 b_mantissa = b_int - (1 << b_bits_left) if grs_int == 3 and operation == OP_DIV: a_mantissa = random.randint(1, (1 << a_bits_left) - 1) - b_mantissa = random.randint(1, (1 << b_bits_left) - 1) + b_mantissa = random.randint(1, a_mantissa) if grs_int != 2 and grs_int != 6 and grs_int != 4 and operation == OP_MUL: while not met_conditions: @@ -455,9 +445,7 @@ def get_grs_mant(operation: str, precision: str, a_exp: int, b_exp: int, hashStr return (bin_a_mantissa, bin_b_mantissa) -def factor_mul_gen( - precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO, grs_pattern: str, sign: str, genTests: bool -) -> tuple[str, str]: +def factor_mul_gen(precision: str, rounding_mode: str, grs_pattern: str, sign: str) -> Iterator[tuple[str, str]]: m_bits = MANTISSA_BITS[precision] e_min = UNBIASED_EXP[precision][0] - 1 e_bits = EXPONENT_BITS[precision] @@ -544,7 +532,6 @@ def factor_mul_gen( FMT_QUAD: 33881219305284356466756909162937, }, } - print(precision) # Randomize the sign bit if sign == "0": a_sign = random.randint(0, 1) @@ -558,7 +545,7 @@ def factor_mul_gen( factor_2 = target_int // factor_1 # Integers have unlimited precision, must use integer division if target_int == 1 and factor_1 == 1: - return ("0", "0") + yield ("0", "0") a_int = int(max(factor_1, factor_2)) # Ensure a is the largest value b_int = int(min(factor_1, factor_2)) @@ -594,12 +581,7 @@ def factor_mul_gen( a_fp = generate_FP(e_bits, str(a_sign), a_exp, a_bin, e_bias) b_fp = generate_FP(e_bits, str(b_sign), b_exp, b_bin, e_bias) - if genTests: - run_and_store_test_vector( - f"{OP_MUL}_{rounding_mode}_{a_fp}_{b_fp}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f - ) - - return (a_fp, b_fp) + yield (a_fp, b_fp) def mul_div_grs_gen( @@ -609,11 +591,8 @@ def mul_div_grs_gen( grs: str, g_exp: int, sign: str, - test_f: TextIO, - cover_f: TextIO, hashEnding: str, - genTests: bool, -) -> tuple[str, str]: +) -> Iterator[tuple[str, str]]: hashString = "b5" + OP_MUL + precision + rounding_mode + hashEnding seed(hashString) @@ -635,8 +614,6 @@ def mul_div_grs_gen( grs_int = int(grs, 2) - print("GRS_INT:", grs_int) - target_exp = first_bit if grs_int == 1: smallest_res_exp = min_exp - (2 * m_bits) @@ -655,27 +632,21 @@ def mul_div_grs_gen( a = generate_FP(e_bits, str(a_sign), a_exp, a_mant, e_bias) b = generate_FP(e_bits, str(b_sign), b_exp, b_mant, e_bias) - - if genTests: - run_and_store_test_vector( - f"{operation}_{rounding_mode}_{a}_{b}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f - ) - - return (a, b) + yield (a, b) -def tests_multiply_1_2(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: +def tests_multiply_1_2(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]]: hashString = "b5" + OP_MUL + precision + rounding_mode seed(hashString) min_exp = UNBIASED_EXP[precision][0] sn_exp = min_exp - 1 - mul_div_grs_gen(OP_MUL, precision, rounding_mode, "001", sn_exp, "0", test_f, cover_f, "1/2", True) # Positive Test - mul_div_grs_gen(OP_MUL, precision, rounding_mode, "001", sn_exp, "1", test_f, cover_f, "1/2", True) # Negative Test + yield from mul_div_grs_gen(OP_MUL, precision, rounding_mode, "001", sn_exp, "0", "1/2") + yield from mul_div_grs_gen(OP_MUL, precision, rounding_mode, "001", sn_exp, "1", "1/2") -def tests_multiply_3_4(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: +def tests_multiply_3_4(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]]: hashString = "b5" + OP_MUL + precision + rounding_mode seed(hashString) @@ -684,114 +655,59 @@ def tests_multiply_3_4(precision: str, rounding_mode: str, test_f: TextIO, cover minSNPos = min_exp - m_bits # Treating the minSN as normalized - # minSN - 3 ulp G = 0, R = 0, S = 1 - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "001", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True - ) # Positive Test - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "001", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True - ) # Negative Test + for grs_int in range(1, 8): + # grs_int = 2 + yield from mul_div_grs_gen( + OP_MUL, precision, rounding_mode, f"{grs_int:03b}", minSNPos, "0", f"{grs_int:03b}" + ) # Positive Test + yield from mul_div_grs_gen( + OP_MUL, precision, rounding_mode, f"{grs_int:03b}", minSNPos, "1", f"{grs_int:03b}" + ) # Negative Test - # minSN - 2 ulp G = 0, R = 1, S = 0 - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "010", minSNPos, "0", test_f, cover_f, "minSN-2ulppos", True - ) # Positive Test - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "010", minSNPos, "1", test_f, cover_f, "minSN-2ulpneg", True - ) # Negative Test - # minSN - 1 ulp G = 0, R = 1, S = 1 - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "011", minSNPos, "0", test_f, cover_f, "minSN-1ulppos", True - ) # Positive Test - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "011", minSNPos, "1", test_f, cover_f, "minSN-1ulpneg", True - ) # Negative Test - - # minSN G = 1, R = 0, S = 0 - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "100", minSNPos, "0", test_f, cover_f, "minSNpos", True - ) # Positive Test - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "100", minSNPos, "1", test_f, cover_f, "minSNneg", True - ) # Negative Test - - # minSN + 1 ulp G = 1, R = 0, S = 1 - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "101", minSNPos, "0", test_f, cover_f, "minSN+1ulppos", True - ) # Positive Test - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "101", minSNPos, "1", test_f, cover_f, "minSN+1ulpneg", True - ) # Negative Test - - # minSN + 2 ulp G = 1, R = 1, S = 0 - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "110", minSNPos, "0", test_f, cover_f, "minSN+2ulppos", True - ) # Positive Test - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "110", minSNPos, "1", test_f, cover_f, "minSN+2ulpneg", True - ) # Negative Test - - # minSN + 3 ulp G = 1, R = 1, S = 1 - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "111", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True - ) # Positive Test - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "111", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True - ) # Negative Test - - -def tests_multiply_5_6(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: +def tests_multiply_5_6(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]]: min_exp = UNBIASED_EXP[precision][0] # MinNorm - 3 ulp - factor_mul_gen(precision, rounding_mode, test_f, cover_f, "101", "0", True) # Positive Test - factor_mul_gen(precision, rounding_mode, test_f, cover_f, "101", "1", True) # Negative Test + yield from factor_mul_gen(precision, rounding_mode, "101", "0") # Positive Test + yield from factor_mul_gen(precision, rounding_mode, "101", "1") # Negative Test # MinNorm - 2 ulp - factor_mul_gen(precision, rounding_mode, test_f, cover_f, "110", "0", True) # Positive Test - factor_mul_gen(precision, rounding_mode, test_f, cover_f, "110", "1", True) # Negative Test + yield from factor_mul_gen(precision, rounding_mode, "110", "0") # Positive Test + yield from factor_mul_gen(precision, rounding_mode, "110", "1") # Negative Test # MinNorm - 1 ulp - factor_mul_gen(precision, rounding_mode, test_f, cover_f, "111", "0", True) # Positive Test - factor_mul_gen(precision, rounding_mode, test_f, cover_f, "111", "1", True) # Negative Test + yield from factor_mul_gen(precision, rounding_mode, "111", "0") # Positive Test + yield from factor_mul_gen(precision, rounding_mode, "111", "1") # Negative Test # MinNorm - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "100", min_exp, "0", test_f, cover_f, "minExp", True - ) # Positive Test - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "100", min_exp, "1", test_f, cover_f, "minExp", True - ) # Negative Test + yield from mul_div_grs_gen(OP_MUL, precision, rounding_mode, "100", min_exp, "0", "minExp") # Positive Test + yield from mul_div_grs_gen(OP_MUL, precision, rounding_mode, "100", min_exp, "1", "minExp") # Negative Test # MinNorm + 1 ulp - factor_mul_gen(precision, rounding_mode, test_f, cover_f, "001", "0", True) # Positive Test - factor_mul_gen(precision, rounding_mode, test_f, cover_f, "001", "1", True) # Negative Test + yield from factor_mul_gen(precision, rounding_mode, "001", "0") # Positive Test + yield from factor_mul_gen(precision, rounding_mode, "001", "1") # Negative Test # MinNorm + 2 ulp - factor_mul_gen(precision, rounding_mode, test_f, cover_f, "010", "0", True) # Positive Test - factor_mul_gen( - precision, rounding_mode, test_f, cover_f, "010", "1", True - ) # Negative Test #BF_16 is producing an error + yield from factor_mul_gen(precision, rounding_mode, "010", "0") # Positive Test + yield from factor_mul_gen(precision, rounding_mode, "010", "1") # Negative Test #BF_16 is producing an error # MinNorm + 3 ulp - factor_mul_gen(precision, rounding_mode, test_f, cover_f, "011", "0", True) # Positive Test - factor_mul_gen( - precision, rounding_mode, test_f, cover_f, "011", "1", True - ) # Negative Test #FP_128 is producing an error + yield from factor_mul_gen(precision, rounding_mode, "011", "0") # Positive Test + yield from factor_mul_gen(precision, rounding_mode, "011", "1") # Negative Test #FP_128 is producing an error -def tests_multiply_7_8(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: +def tests_multiply_7_8(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]]: m_bits = MANTISSA_BITS[precision] min_exp = UNBIASED_EXP[precision][0] target_exp = min_exp - m_bits - mul_div_grs_gen(OP_MUL, precision, rounding_mode, "011", target_exp, "0", test_f, cover_f, "1/2 pos", True) - mul_div_grs_gen(OP_MUL, precision, rounding_mode, "011", target_exp, "1", test_f, cover_f, "1/2 neg", True) + yield from mul_div_grs_gen(OP_MUL, precision, rounding_mode, "011", target_exp, "0", "1/2 pos") + yield from mul_div_grs_gen(OP_MUL, precision, rounding_mode, "011", target_exp, "1", "1/2 neg") -def tests_multiply_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: +def tests_multiply_9(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]]: min_exp = UNBIASED_EXP[precision][0] # all values from minNorm.exp to minNorm.exp + 5 @@ -802,20 +718,37 @@ def tests_multiply_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f seed("b5" + OP_MUL + precision + rounding_mode + str(target_exp)) sign = str(random.randint(0, 1)) - mul_div_grs_gen( - OP_MUL, precision, rounding_mode, "011", target_exp, sign, test_f, cover_f, str(target_exp), True - ) + yield from mul_div_grs_gen(OP_MUL, precision, rounding_mode, "011", target_exp, sign, str(target_exp)) + + +# def multiplyTests(test_f: TextIO, cover_f: TextIO, genTests: bool) -> None: +# if genTests: +# for precision in FLOAT_FMTS: +# for rounding_mode in ROUNDING_MODES: +# tests_multiply_1_2(precision, rounding_mode, test_f, cover_f) +# tests_multiply_3_4(precision, rounding_mode, test_f, cover_f) +# tests_multiply_5_6(precision, rounding_mode, test_f, cover_f) +# tests_multiply_7_8(precision, rounding_mode, test_f, cover_f) +# tests_multiply_9(precision, rounding_mode, test_f, cover_f) + + +def getMultiplyTests(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]]: + yield from tests_multiply_1_2(precision, rounding_mode) + yield from tests_multiply_3_4(precision, rounding_mode) + yield from tests_multiply_5_6(precision, rounding_mode) + yield from tests_multiply_7_8(precision, rounding_mode) + yield from tests_multiply_9(precision, rounding_mode) def multiplyTests(test_f: TextIO, cover_f: TextIO) -> None: for precision in FLOAT_FMTS: - # precision = FMT_SINGLE #TODO: Alter to have all precisions for rounding_mode in ROUNDING_MODES: - # tests_multiply_1_2(precision, rounding_mode, test_f, cover_f) - # tests_multiply_3_4(precision, rounding_mode, test_f, cover_f) - tests_multiply_5_6(precision, rounding_mode, test_f, cover_f) - # tests_multiply_7_8(precision, rounding_mode, test_f, cover_f) - # tests_multiply_9(precision, rounding_mode, test_f, cover_f) + for a, b in getMultiplyTests(precision, rounding_mode): + run_and_store_test_vector( + f"{OP_MUL}_{rounding_mode}_{a}_{b}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", + test_f, + cover_f, + ) def fma_gen( @@ -851,59 +784,49 @@ def fma_gen( c_sign = (op_add_sign + addend_sign) % 2 # Generate the multiplication testvectors - a, b = mul_div_grs_gen( - OP_MUL, - precision, - rounding_mode, - product_grs, - product_exponent, - str(mul_sign), - test_f, - cover_f, - hashEnding, - False, - ) - - # Generate the addition testvector - c_exp = -1 - c_mant = -1 - if addend_pattern == "1*m-1_0": - c_exp = min_exp - 1 - c_mant = (1 << m_bits) - 2 # all ones throughout the mantissa - elif addend_pattern == "min_n": - c_exp = min_exp - c_mant = 0 - elif addend_pattern == "2*min_sn": - c_exp = min_exp - 1 - c_mant = 2 - elif addend_pattern == "min_sn": - c_exp = min_exp - 1 - c_mant = 1 - elif addend_pattern == "1_0*m-1_1": - c_exp = min_exp - c_mant = 1 - elif addend_pattern == "rand_sn": - c_exp = min_exp - random.randint(1, m_bits) # Upper: Just SN, Lower: Leave 1 spot open on the end - max_mant = (1 << (min_exp - c_exp)) - 1 - c_mant = random.randint(1, max_mant) - c_mant = max_mant - # Normalize c_exp - c_exp = max(min_exp - 1, c_exp) - elif addend_pattern == "rand_n": - c_exp = min_exp - max_mant = 1 << m_bits - c_mant = random.randint(1, max_mant) - elif addend_pattern[0 : addend_pattern.index("_")] == "randexp": - exp_string = addend_pattern[addend_pattern.index("_") + 1 : len(addend_pattern)] - c_exp = 0 - int(exp_string) - max_mant = (1 << m_bits) - 1 - c_mant = random.randint(1, max_mant) - - c_bin_mant = f"{c_mant:0{m_bits}b}" - c = generate_FP(e_bits, str(c_sign), c_exp, c_bin_mant, e_bias) - run_and_store_test_vector( - f"{operation}_{rounding_mode}_{a}_{b}_{c}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f - ) + for a, b in mul_div_grs_gen( + OP_MUL, precision, rounding_mode, product_grs, product_exponent, str(mul_sign), hashEnding + ): + # Generate the addition testvector + c_exp = -1 + c_mant = -1 + if addend_pattern == "1*m-1_0": + c_exp = min_exp - 1 + c_mant = (1 << m_bits) - 2 # all ones throughout the mantissa + elif addend_pattern == "min_n": + c_exp = min_exp + c_mant = 0 + elif addend_pattern == "2*min_sn": + c_exp = min_exp - 1 + c_mant = 2 + elif addend_pattern == "min_sn": + c_exp = min_exp - 1 + c_mant = 1 + elif addend_pattern == "1_0*m-1_1": + c_exp = min_exp + c_mant = 1 + elif addend_pattern == "rand_sn": + c_exp = min_exp - random.randint(1, m_bits) # Upper: Just SN, Lower: Leave 1 spot open on the end + max_mant = (1 << (min_exp - c_exp)) - 1 + c_mant = random.randint(1, max_mant) + c_mant = max_mant + # Normalize c_exp + c_exp = max(min_exp - 1, c_exp) + elif addend_pattern == "rand_n": + c_exp = min_exp + max_mant = 1 << m_bits + c_mant = random.randint(1, max_mant) + elif addend_pattern[0 : addend_pattern.index("_")] == "randexp": + exp_string = addend_pattern[addend_pattern.index("_") + 1 : len(addend_pattern)] + c_exp = 0 - int(exp_string) + max_mant = (1 << m_bits) - 1 + c_mant = random.randint(1, max_mant) + + c_bin_mant = f"{c_mant:0{m_bits}b}" + c = generate_FP(e_bits, str(c_sign), c_exp, c_bin_mant, e_bias) + run_and_store_test_vector( + f"{operation}_{rounding_mode}_{a}_{b}_{c}_{precision}_{32 * '0'}_{precision}_00", test_f, cover_f + ) def tests_fma_1_2(operation: str, precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: @@ -1115,110 +1038,85 @@ def div_grs_mant( return a_fp, b_fp -def tests_div_1_2(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: +def tests_div_1_2(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]]: min_exp = UNBIASED_EXP[precision][0] # Random SN: G = 0, R = 0, S = 1 - mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", min_exp + 1, "0", test_f, cover_f, "positive", True) - mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", min_exp + 1, "0", test_f, cover_f, "positive", True) + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", min_exp + 1, "0", "positive") + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", min_exp + 1, "1", "positive") -def tests_div_3_4(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: +def tests_div_3_4(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]]: m_bits = MANTISSA_BITS[precision] min_exp = UNBIASED_EXP[precision][0] minSNPos = min_exp - m_bits # Treating the minSN as normalized # minSN - 3 ulp G = 0, R = 0, S = 1 - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "001", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True - ) # Positive Test - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "001", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True - ) # Negative Test - - # # minSN - 2 ulp G = 0, R = 1, S = 0 - # mul_div_grs_gen( - # OP_DIV, precision, rounding_mode, "010", minSNPos, "0", test_f, cover_f, "minSN-2ulppos", True - # ) # Positive Test - # mul_div_grs_gen( - # OP_DIV, precision, rounding_mode, "010", minSNPos, "1", test_f, cover_f, "minSN-2ulpneg", True - # ) # Negative Test - - # # minSN - 1 ulp G = 0, R = 1, S = 1 - # mul_div_grs_gen( - # OP_DIV, precision, rounding_mode, "011", minSNPos, "0", test_f, cover_f, "minSN-1ulppos", True - # ) # Positive Test - # mul_div_grs_gen( - # OP_DIV, precision, rounding_mode, "011", minSNPos, "1", test_f, cover_f, "minSN-1ulpneg", True - # ) # Negative Test - - # # minSN G = 1, R = 0, S = 0 - # mul_div_grs_gen( - # OP_DIV, precision, rounding_mode, "100", minSNPos, "0", test_f, cover_f, "minSNpos", True - # ) # Positive Test - # mul_div_grs_gen( - # OP_DIV, precision, rounding_mode, "100", minSNPos, "1", test_f, cover_f, "minSNneg", True - # ) # Negative Test - - # # minSN + 1 ulp G = 1, R = 0, S = 1 - # mul_div_grs_gen( - # OP_DIV, precision, rounding_mode, "101", minSNPos, "0", test_f, cover_f, "minSN+1ulppos", True - # ) # Positive Test - # mul_div_grs_gen( - # OP_DIV, precision, rounding_mode, "101", minSNPos, "1", test_f, cover_f, "minSN+1ulpneg", True - # ) # Negative Test - - # # minSN + 2 ulp G = 1, R = 1, S = 0 - # mul_div_grs_gen( - # OP_DIV, precision, rounding_mode, "110", minSNPos, "0", test_f, cover_f, "minSN+2ulppos", True - # ) # Positive Test - # mul_div_grs_gen( - # OP_DIV, precision, rounding_mode, "110", minSNPos, "1", test_f, cover_f, "minSN+2ulpneg", True - # ) # Negative Test - - # # minSN + 3 ulp G = 1, R = 1, S = 1 - # mul_div_grs_gen( - # OP_DIV, precision, rounding_mode, "111", minSNPos, "0", test_f, cover_f, "minSN-3ulppos", True - # ) # Positive Test - # mul_div_grs_gen( - # OP_DIV, precision, rounding_mode, "111", minSNPos, "1", test_f, cover_f, "minSN-3ulpneg", True - # ) # Negative Test - - -def tests_div_7_8(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", minSNPos, "0", "minSN-3ulppos") + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", minSNPos, "1", "minSN-3ulppos") + + # minSN - 2 ulp G = 0, R = 1, S = 0 + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "010", minSNPos, "0", "minSN-2ulppos") + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "010", minSNPos, "1", "minSN-2ulppos") + + # minSN - 1 ulp G = 0, R = 1, S = 1 + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "011", minSNPos, "0", "minSN-1ulppos") + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "011", minSNPos, "1", "minSN-1ulppos") + + # minSN G = 1, R = 0, S = 0 + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "100", minSNPos, "0", "minSNppos") + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "100", minSNPos, "1", "minSNpos") + + # minSN + 1 ulp G = 1, R = 0, S = 1 grs_int = 5 + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "101", minSNPos, "0", "minSN") + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "101", minSNPos, "1", "minSN") + + # minSN + 2 ulp G = 1, R = 1, S = 0 + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "110", minSNPos, "0", "minSN+2ulppos") + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "110", minSNPos, "1", "minSN+2ulppos") + + # minSN + 3 ulp G = 1, R = 1, S = 1 + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "111", minSNPos, "0", "minSN+3ulppos") + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "111", minSNPos, "1", "minSN+3ulppos") + + +def tests_div_7_8(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]]: m_bits = MANTISSA_BITS[precision] min_exp = UNBIASED_EXP[precision][0] minSNPos = min_exp - m_bits # Treating the minSN as normalized - mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", minSNPos, "0", test_f, cover_f, "minSN", True) - mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", minSNPos, "1", test_f, cover_f, "minSN", True) + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", minSNPos, "0", "minSN") + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "001", minSNPos, "1", "minSN") -def tests_div_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: +def tests_div_9(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]]: min_exp = UNBIASED_EXP[precision][0] - min_exp_range = min_exp - 1 # SN exp, or minNorm.exp - max_exp_range = min_exp_range + 5 - - for target_exp in range(min_exp_range, max_exp_range + 1): # Because end is exclusive + for target_exp in range(min_exp, min_exp + 6): # Because end is exclusive seed("b5" + OP_DIV + precision + rounding_mode + str(target_exp)) sign = str(random.randint(0, 1)) - mul_div_grs_gen( - OP_DIV, precision, rounding_mode, "011", target_exp + 1, sign, test_f, cover_f, str(target_exp), True - ) + yield from mul_div_grs_gen(OP_DIV, precision, rounding_mode, "011", target_exp + 1, sign, "min") + + +def getDivTests(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]]: + yield from tests_div_1_2(precision, rounding_mode) + yield from tests_div_3_4(precision, rounding_mode) + yield from tests_div_7_8(precision, rounding_mode) + # yield from tests_div_9(precision, rounding_mode) def divTests(test_f: TextIO, cover_f: TextIO) -> None: - # for precision in FLOAT_FMTS: - precision = FMT_BF16 - for rounding_mode in ROUNDING_MODES: - # tests_div_1_2(precision, rounding_mode, test_f, cover_f) - tests_div_3_4(precision, rounding_mode, test_f, cover_f) - # tests_div_7_8(precision, rounding_mode, test_f, cover_f) - # tests_div_9(precision, rounding_mode, test_f, cover_f) + for precision in FLOAT_FMTS: + for rounding_mode in ROUNDING_MODES: + for a, b in getDivTests(precision, rounding_mode): + run_and_store_test_vector( + f"{OP_DIV}_{rounding_mode}_{a}_{b}_{32 * '0'}_{precision}_{32 * '0'}_{precision}_00", + test_f, + cover_f, + ) def tests_add_sub_1_2(precision: str, rounding_mode: str, test_f: TextIO, cover_f: TextIO) -> None: @@ -1371,14 +1269,11 @@ def main() -> None: Path("./tests/testvectors/B5_tv.txt").open("w") as test_f, Path("./tests/covervectors/B5_cv.txt").open("w") as cover_f, ): - # convertTests(test_f, cover_f) - # multiplyTests(test_f, cover_f) - # addSubTests(test_f, cover_f) - # fmaTests(test_f, cover_f) + convertTests(test_f, cover_f) + multiplyTests(test_f, cover_f) + addSubTests(test_f, cover_f) + fmaTests(test_f, cover_f) divTests(test_f, cover_f) - # input_filename = "./tests/covervectors/B5_cv.txt" - # output_filename = "./tests/readable/decoded_results.txt" - # softfloat_parser.parse_test_vectors(TestRange=(1, 50), file=input_filename, output_file=output_filename) if __name__ == "__main__": diff --git a/src/cover_float/testgen/__init__.py b/src/cover_float/testgen/__init__.py index e5d05aa..ebeea90 100644 --- a/src/cover_float/testgen/__init__.py +++ b/src/cover_float/testgen/__init__.py @@ -12,6 +12,7 @@ import cover_float.testgen.B13 as B13 import cover_float.testgen.B14 as B14 import cover_float.testgen.B15 as B15 +import cover_float.testgen.B18 as B18 import cover_float.testgen.B20 as B20 import cover_float.testgen.B21 as B21 import cover_float.testgen.B25 as B25 @@ -34,6 +35,7 @@ "B13", "B14", "B15", + "B18", "B20", "B21", "B25", From 9de8653630bc418823af86e3df2a6e2f762155b8 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Wed, 6 May 2026 22:14:46 -0700 Subject: [PATCH 20/21] B18 running tests --- Makefile | 2 +- config.svh | 12 ++++++------ src/cover_float/testgen/B18.py | 28 +++++++++++----------------- src/cover_float/testgen/B5.py | 23 ++++++++--------------- 4 files changed, 26 insertions(+), 39 deletions(-) diff --git a/Makefile b/Makefile index 60e40dc..edf6ccb 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ ifeq ($(AGGRESSIVENESS), 0) COVER_FLOAT_FLAGS += --partial-output endif -MODELS := B1 B2 B3 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 B16 B18 B20 B21 B25 B26 B27 B29 +MODELS := B1 B2 B3 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 B16 B18 B20 B21 B25 B26 B27 B29 .PHONY: build clean sim all $(MODELS) diff --git a/config.svh b/config.svh index 6cfa0b6..8ff022e 100644 --- a/config.svh +++ b/config.svh @@ -36,15 +36,15 @@ `define COVER_B18 // `define COVER_B19 // `define COVER_B20 -`define COVER_B21 -`define COVER_B22 -`define COVER_B23 -`define COVER_B24 +// `define COVER_B21 +// `define COVER_B22 +// `define COVER_B23 +// `define COVER_B24 // `define COVER_B25 // `define COVER_B26 // `define COVER_B27 -`define COVER_B28 -`define COVER_B29 +// `define COVER_B28 +// `define COVER_B29 // define macros for which precisions to check coverage for // e.g. `define COVER_F32 for single precision diff --git a/src/cover_float/testgen/B18.py b/src/cover_float/testgen/B18.py index 32baa98..ec3336c 100644 --- a/src/cover_float/testgen/B18.py +++ b/src/cover_float/testgen/B18.py @@ -2,7 +2,6 @@ # Lamarr import random -from pathlib import Path from typing import TextIO from cover_float.common.constants import ( @@ -28,6 +27,7 @@ ) from cover_float.reference import run_and_store_test_vector from cover_float.testgen.B5 import getMultiplyTests +from cover_float.testgen.model import register_model B5_FMTS = [FMT_QUAD, FMT_DOUBLE, FMT_SINGLE, FMT_BF16, FMT_HALF] ROUNDING_MODES = [ROUND_NEAR_EVEN, ROUND_MINMAG, ROUND_MIN, ROUND_MAX, ROUND_NEAR_MAXMAG] @@ -92,10 +92,11 @@ def genUnderflowTests(test_f: TextIO, cover_f: TextIO) -> None: for precision in FLOAT_FMTS: min_exp = UNBIASED_EXP[precision][0] + 1 max_exp = UNBIASED_EXP[precision][1] - 1 - for rounding_mode in ROUNDING_MODES: - for a, b in getMultiplyTests(precision, rounding_mode): - for operation in FMA_OPS: - c = getRandomInt(min_exp, max_exp, str(random.randint(0, 1)), precision) + rounding_mode = random.choice(ROUNDING_MODES) + for a, b in getMultiplyTests(precision, rounding_mode): + for operation in FMA_OPS: + for sign in ["0", "1"]: + c = getRandomInt(min_exp, max_exp, sign, precision) run_and_store_test_vector( f"{operation}_{rounding_mode}_{a}_{b}_{c}_{precision}_{32 * '0'}_{precision}_00", test_f, @@ -329,15 +330,8 @@ def get_fp_values(precision: str, grs_pattern: str, mul_sign: int, addend_sign: return a_fp, b_fp, c_fp -def main() -> None: - with ( - Path("./tests/testvectors/B18_tv.txt").open("w") as test_f, - Path("./tests/covervectors/B18_cv.txt").open("w") as cover_f, - ): - genUnderflowTests(test_f, cover_f) - # overFlowTests(test_f, cover_f) - lsbGuardStickyTests(test_f, cover_f) - - -if __name__ == "__main__": - main() +@register_model("B18") +def main(test_f: TextIO, cover_f: TextIO) -> None: + genUnderflowTests(test_f, cover_f) + # overFlowTests(test_f, cover_f) + lsbGuardStickyTests(test_f, cover_f) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 310b4ec..554292f 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -4,7 +4,6 @@ import random from collections.abc import Iterator -from pathlib import Path from random import seed from typing import TextIO @@ -36,6 +35,7 @@ ) from cover_float.common.util import reproducible_hash from cover_float.reference import run_and_store_test_vector +from cover_float.testgen.model import register_model B5_FMTS = [FMT_QUAD, FMT_DOUBLE, FMT_SINGLE, FMT_BF16, FMT_HALF] ROUNDING_MODES = [ROUND_NEAR_EVEN, ROUND_MINMAG, ROUND_MIN, ROUND_MAX, ROUND_NEAR_MAXMAG] @@ -1264,17 +1264,10 @@ def addSubTests(test_f: TextIO, cover_f: TextIO) -> None: tests_add_sub_9(precision, rounding_mode, test_f, cover_f) -def main() -> None: - with ( - Path("./tests/testvectors/B5_tv.txt").open("w") as test_f, - Path("./tests/covervectors/B5_cv.txt").open("w") as cover_f, - ): - convertTests(test_f, cover_f) - multiplyTests(test_f, cover_f) - addSubTests(test_f, cover_f) - fmaTests(test_f, cover_f) - divTests(test_f, cover_f) - - -if __name__ == "__main__": - main() +@register_model("B5") +def main(test_f: TextIO, cover_f: TextIO) -> None: + convertTests(test_f, cover_f) + multiplyTests(test_f, cover_f) + addSubTests(test_f, cover_f) + fmaTests(test_f, cover_f) + divTests(test_f, cover_f) From a4ec94e23c0b8a4c1cee23af09371e095e87eb72 Mon Sep 17 00:00:00 2001 From: Lamarr Olive Date: Sun, 31 May 2026 21:29:08 -0700 Subject: [PATCH 21/21] updated B18/B5 and docs --- docs/B18.adoc | 93 ++++++++++ docs/B5.adoc | 312 +++++++++++++++++++++++++++++++++ docs/B6.adoc | 2 +- src/cover_float/testgen/B18.py | 21 ++- src/cover_float/testgen/B5.py | 45 ++--- 5 files changed, 432 insertions(+), 41 deletions(-) create mode 100644 docs/B18.adoc create mode 100644 docs/B5.adoc diff --git a/docs/B18.adoc b/docs/B18.adoc new file mode 100644 index 0000000..4bc21f2 --- /dev/null +++ b/docs/B18.adoc @@ -0,0 +1,93 @@ += B18 Documentation +:toc: +:toclevels: 3 +:sectnums: +:stem: + +== B18: Multiply-Add: Special Events + +Aharoni et al. + +=== Description +This model checks different cases where the multipliccaion causes some event in the product while the addition cancels this event + +Test 1):: Product: Enumerate on all options for LSB, Guard, and Sticky bit. +Intermediate Result: Exact (Guard and Sticky are zero) + +Test 2):: Product: Take overflow values from (B4) "Overflow". +Intermediate Result: No overflow + +Test 3):: Product: Take underflow values from model (B5) "Underflow". +Intermediate Result: No underflow + +*Number of tests:* 1 E 6 + +*Precisions Supported:* `BF_16`, `FP_16`, `FP_32`, `FP_64`, `FP_128` + +*Operations Supported:* `All` + +== Implementation + +=== Definitions +`min_exp`:: The minimum exponent for each precision, where the biased exponent value is 1. +`lp/hp`:: As specified within the operations section, this test suite will be performing high to low precision converts. `hp` refers to the higher precision input operand for these operations, `lp` refers to the lower precision output of the conversion. +`m_bits`:: The number of mantissa bits, dependent on the precision. +`min_sn_exp`:: The smallest possible subnormal value based on the precision, 2^min_exp - m_bits. +`min_sn`:: The smallest possible subnormal input, 2^(min_exp - m_bits) +`hp_exp/lp_exp`:: The exponent value for `hp` and `lp` respectively. +`lp_sn_exp`:: The exponent value corresponding to a subnormal value for `lp`. In any precision, this is 0 when represented as a biased exponent. In most tests, the mathematical operations will be expressed where `lp_sn_exp` is unbiased. +`lsb_exp`:: The exponent value for the least significant bit. +`lrs_int`:: The integer representation of the desired Isb, rounding, and guard bit values. + +== Operations +=== Three operands +* FMADD +* FMSUB +* FNMSUB +* FNMADD + +=== General Implementation +Each test presents a different requirement on the intermediate result and product. Based on this requirement, the appropriate factors for multiplication are chosen and the effective operation is determined. The rounding mode is randomly chosen. + +==== Test 1 Product Mantissa Pattern +[cols="5*"] +|=== +| lrs_int | LSB | Rounding | Sticky | Intermediate Mantissa + +| 1 | 0 | 0 | 1 | pass:[****_****_** 1*] +| 2 | 0 | 1 | 0 | pass:[****_****_** 10] +| 3 | 0 | 1 | 1 | pass:[****_****_** 1*] +| 4 | 1 | 0 | 0 | pass:[****_****_*1 00] +| 5 | 1 | 0 | 1 | pass:[****_****_*1 0*] +| 6 | 1 | 1 | 0 | pass:[****_****_*1 10] +| 7 | 1 | 1 | 1 | pass:[****_****_*1 1*] +| 8 | 0 | 0 | 0 | pass:[****_****_*0 00] + +|=== + +==== Test 1 Intermediate Mantissa Pattern +[cols="4*"] +|=== +|LSB | Rounding | Sticky | Intermediate Mantissa + +| * | 0 | 0 | pass:[****_****_** 00] + +|=== + + +== Specific Test Procedure +=== Test 1 +Multiplication products were extracted from B5, any random number was added or subtracted with a random sign and random operation. Rounding mode is randomly chosen. + +=== Total Test Count +[cols="3*", options="header"] +|=== +| Test # | Breakdown | Count + +| 1 | (188 * 4) | 752 +| 2 | incomplete | incomplete +| 3 | incomplete | incomplete +| *Total Tests* | *700* +|=== + +_*Differs from Aharoni test count_ diff --git a/docs/B5.adoc b/docs/B5.adoc new file mode 100644 index 0000000..276b055 --- /dev/null +++ b/docs/B5.adoc @@ -0,0 +1,312 @@ += B5 Documentation +:toc: +:toclevels: 3 +:sectnums: +:stem: + +== B5: Underflow and Near Underflow + +Aharoni et al. + +=== Description +This model creates a test-case for each of the following constraints on the intermediate results: + +Test 1):: A random positive SubNorm + +Test 2):: A random negative SubNorm + +Test 3):: All numbers in the range [+ MinSubNorm - 3 ulp, + MinSubNorm + 3 ulp] + +Test 4):: All numbers in the range [- MinSubNorm - 3 ulp, - MinSubNorm + 3 ulp] + +Test 5):: All numbers in the range [ + MinNorm - 3 ulp, + MinNorm + 3 ulp] + +Test 6):: All numbers in the range [- MinNorm - 3 ulp, - MinNorm + 3 ulp] + +Test 7):: A random number in the range (0, MinSubNorm) + +Test 8):: A random numnber in the range (-MinSubNorm, -0) + +Test 9):: One number for every exponent in the range [MinNorm.exp, MinNorm.exp + 5] + +*Number of tests:* 2 E 5 + +*Precisions Supported:* `BF_16`, `FP_16`, `FP_32`, `FP_64`, `FP_128` + +*Operations Supported:* `All` + +== Implementation + +=== Definitions +`min_exp`:: The minimum normal exponent for each precision, where the biased exponent value is 1. +`lp/hp`:: This test suite will be performing high to low precision converts to trigger underflow. hp refers to the input operand for these operations, the higher precision input. lp refers to the lower precision output of the conversion. +`hp_exp/lp_exp`:: The exponent value for hp and lp respectively. +`lp_sn_exp`:: The exponent value corresponding to a subnormal value for lp. In any precision, this is 0 when represented as a biased exponent. In most tests, the mathematical operations will be expressed where lp_sn_exp is unbiased. +`lp_max_sn`:: The largest possible subnormal value for the lp, the exponent is lp_sn_exp, the mantissa is all 1s. +`lp_min_sn`:: The smallest representable subnormal value for lp, the exponent is lp_sn_exp, the mantissa is all 0s except for a 1 in the ulp. +`m_bits`:: The number of mantissa bits, depends on the precision. +lp_m_bits/hp_m_bits: The number of m_bits for lp/hp, respectively. +`hp_min_exp`:: The lowest exponent value for hp in order to trigger underflow after a conversion. +`hp_max_exp`:: The highest exponent value for hp in order to trigger underflow after a conversion. +`ulp`:: Aharoni defines 1 ulp as “the value of a 1 in the last place of a number representation. For the binary floating-point number: b0.b1b2…bk * 2m, ulp = 1 * 2m-k” (Aharoni, 10). Within this definition, m represents the bit number within the mantissa and k represents the unbiased exponent. While this definition of ulp is utilized within Aharoni’s description of tests 3 - 6, in the B5 test suite, it is not always possible to achieve a value of -3 ulp. Additionally, B5 is testing various intermediate values and the definition of m used within the Aharoni definition is dependent on the operation being performed and the precision involved. The interesting test cases for underflow are where the values of the effective least significant bit (L’), rounding bit (R’), and fist sticky bit (T’) can alter the result after rounding. Based on the rounding mode, various combinations of L’, R’, and T’ can trigger the RND representable, which truncates the value and adds 1 to L’ (RISC-V System-on-Chip Design, by Harris, Stine, Thompson, and Harris). Therefore, the definition, ulp was added to represent the various representations of L’, R’, and T’ which trigger the RND rounding function based on the rounding mode. ulp is defined as 1 * 2m-k-2 where m represents the exponent and k represents the mantissa bits based on the precision. This is the first bit of the sticky bit, 2 positions beneath the lsb. +`msb_exp`:: The exponent value for the most significant bit. + +== Operations + +=== One operand +* FP_128 -> FP_64 | FP_128 -> FP_32 | FP_128 → FP_16 | FP_128 → BF_16 +* FP_64 -> FP_32 | FP_64 -> FP_16 | FP_64 -> FP_16|FP_64 -> BF_16 +* FP_32 -> FP_16 | FP_32 -> BF_16 +* FP_16 -> BF_16 + +=== Two operands +* Multiply +* Divide + +=== Three operands +* FMADD +* FMSUB +* FNMSUB +* FNMADD + +=== Rounding Modes +* Round Near Even +* Round To Min Mag +* Round To Min +* Round To Max +* Round Near Max Mag +* Round To Odd + +=== Description +This model will generate test-cases which seek to ensure that a RISC-V processor properly sets the Underflow flag. IEEE 754 defines two events in which an underflow flag can occur. One is the creation of a tiny nonzero result. The other is a loss of accuracy in approximating these tiny results. Regarding the creation of a tiny nonzero result, IEEE 754 defines two ways in which this tiny result can be detected: + +* “After rounding - when a nonzero result computed as though the exponent range were unbounded would lie strictly between +- 2Emin” +* “Before rounding - when a nonzero result computed as though both the exponent range and the precision were unbounded would lie strictly between +-2Emin”. + +Regarding loss of accuracy, IEEE 754 defines the two ways in which a loss of accuracy can be detected: + +* “A denormalization loss - When the delivered result differs from what would have been computed were the exponent range unbounded” +* “An Inexact Result - When the delivered result differs from what would have been computed were both exponent range and precision unbounded (This is the condition called inexact in 7.5)”. + +Loss of accuracy will be defined by an inexact result. An inexact result is detected by monitoring R’ and T’, the effective rounding bit and sticky bit after a possible normalization shift, respectively (RISC-V System-on-Chip Design, by Harris, Stine, Thompson, and Harris). This model includes 158 interesting tests which are tiny and inexact before rounding and not tiny after rounding. This results in underflow flag if tininess is detected before rounding, but no underflow flag if tininess is detected after rounding. + +The following includes a half precision binary representation of the desired mantissa combinations where stem:["m_bits" = 10]. * represents any random series of mantissa bits + +[[MinSubNorm]] +==== MinSubNorm +- 3 ulp +[cols="4*"] +|=== +| ulp increment | msb_exp | biased exp | Intermediate Mantissa + +|- 3 ulp | stem:["min_exp" - "m_bits"] | 0 | 0000_0000_00 1* +|- 2 ulp | stem:["min_exp" - "m_bits"] | 0 | 0000_0000_00 10 +|- 1 ulp | stem:["min_exp" - "m_bits"] | 0 | 0000_0000_00 1* +| 0 ulp | stem:["min_exp" - "m_bits" + 1] | 0 | 0000_0000_01 00 +|+ 1 ulp | stem:["min_exp" - "m_bits" + 1] | 0 | 0000_0000_01 0* +|+ 2 ulp | stem:["min_exp" - "m_bits" + 1] | 0 | 0000_0000_01 10 +|+ 3 ulp | stem:["min_exp" - "m_bits" + 1] | 0 | 0000_0000_01 1* + +|=== + +[[MinNorm]] +==== MinNorm +- 3 ulp +[cols="3*"] +|=== +| ulp increment | biased exp | Intermediate Mantissa + +|- 3 ulp | 0 | 1111_1111_11 01 +|- 2 ulp | 0 | 1111_1111_11 10 +|- 1 ulp | 0 | 1111_1111_11 1* +| 0 ulp | 1 | 0000_0000_01 00 +|+ 1 ulp | 1 | 0000_0000_01 0* +|+ 2 ulp | 1 | 0000_0000_01 10 +|+ 3 ulp | 1 | 0000_0000_01 1* + +|=== +[[Factors]] +==== MinNorm +- 3ulp Mantissa Values +[cols="1,2,4", options="header"] +|=== +| Precision | ULP Increment | Product + +| FMT_BF16 | +1 ulp | stem:[(8 + 1) * (8^2 - 8 + 1)] +| FMT_HALF | +1 ulp | stem:[(2^4 + 1) * ((2^4)^2 - 2^4 + 1)] +| FMT_SINGLE | +1 ulp | stem:[(2^5 + 1) * ((2^5)^4 - (2^5)^3 + (2^5)^2 - 2^5 + 1)] +| FMT_DOUBLE | +1 ulp | stem:[(2^(11) + 1) * ((2^(11))^4 - (2^(11))^3 + (2^(11))^2 - 2^(11) + 1)] +| FMT_QUAD | +1 ulp | stem:[(2^(38) + 1) * (2^(76) - 2^(38) + 1)] + +| FMT_BF16 | +2 ulp | Prime +| FMT_HALF | +2 ulp | stem:[2^(11) + 1] +| FMT_SINGLE | +2 ulp | stem:[(2^8 + 1) * ((2^8)^2 - 2^8 + 1)] +| FMT_DOUBLE | +2 ulp | stem:[2^(53) + 1] +| FMT_QUAD | +2 ulp | stem:[2^(113) + 1] + +| FMT_BF16 | +3 ulp | stem:[2^9 + 2 + 1] +| FMT_HALF | +3 ulp | stem:[2^(13) + 4 + 3] +| FMT_SINGLE | +3 ulp | stem:[2^(26) + 7] +| FMT_DOUBLE | +3 ulp | stem:[2^(55) + 7] +| FMT_QUAD | +3 ulp | stem:[(2^(112) + 2) * (2^(113) - 2)] + +| FMT_BF16 | -1 ulp | stem:[2^(14) - 1] +| FMT_HALF | -1 ulp | stem:[2^(20) - 1] +| FMT_SINGLE | -1 ulp | stem:[2^(46) - 1] +| FMT_DOUBLE | -1 ulp | stem:[2^(104) - 1] +| FMT_QUAD | -1 ulp | stem:[2^(224) - 1] + +| FMT_BF16 | -2 ulp | stem:[2^8 - 1] +| FMT_HALF | -2 ulp | stem:[2^(11) - 1] +| FMT_SINGLE | -2 ulp | stem:[2^(24) - 1] +| FMT_DOUBLE | -2 ulp | stem:[2^(53) - 1] +| FMT_QUAD | -2 ulp | stem:[2^(113) - 1] + +| FMT_BF16 | -3 ulp | stem:[19 * 107] +| FMT_HALF | -3 ulp | stem:[233 * 281] +| FMT_SINGLE | -3 ulp | stem:[479 * 70051] +| FMT_DOUBLE | -3 ulp | stem:[497401731493 * 36217] +| FMT_QUAD | -3 ulp | stem:[613 * 33881219305284356466756909162937] +|=== +== Specific Test Procedure + +=== Operation: Multiplication +==== General Procedure +Multiplication of floating point numbers `a` and `b` to get `c` takes the following form: stem:[(1."mantissa_a") * (1."mantissa_b") * 2^("a_exp" + "b_exp") * -1^("a_sign" oplus "b_sign") = "c_mantissa" * 2^"c_exp" * -1^"c_sign"]. Each test has a different requirement on `c` which dictates the possible values for the input values `a` and `b`. + +==== Tests 1, 2, 3, and 4 +For tests 1 and 2, stem:["c_exp" <= "minExp"]. For tests 3 and 4, stem:["c_exp" = "msb_exp"] as listed in xref:MinSubNorm[MinSubNorm ± 3 ulp]. The values `a_exp` and `b_exp` must satisfy the equation: stem:["a_exp" + "b_exp" = "c_exp"]. To satisfy this equation, stem:["a_exp" = "random(min_sn, c_exp - min_sn)"] such that the largest value `b_exp` can be is stem:["target" + "min_sn"]. Therefore, stem:["b_exp" = "c_exp" - "a_exp"]. +Tests 1 and 2 have no requirements on the mantissa, so `a_mantissa` and `b_mantissa` are randomly generated values which do not result in a normalization shift. +`a_mantissa` and `b_mantissa` are randomly generated values which result in multiplication as indicated by xref:MinSubNorm[MinSubNorm ± 3 ulp]. +`a_sign` and `b_sign` are randomly generated based on the test's requirements on the `c_sign`.` + +==== Tests 5 and 6 +`a_exp` and `b_exp` are randomly generated such that stem:["a_exp" + "b_exp" = "c_exp"] where `c_exp` matches the exponent value indicated by xref:MinNorm[MinNorm ± 3 ulp]. +Each `c_mantissa` pattern indicated within xref:MinNorm[MinNorm ± 3 ulp] was determined as specified by xref:Factors[Tests 5 and 6 Specific Products]. BF_16 + 2 ulp was excluded because it is prime. +`a_sign` and `b_sign` are randomly generated based on each test's requirements on `c_sign`. + +==== Tests 7 and 8 +`a_exp` and `b_exp` are randomly generated such that stem:["a_exp" + "b_exp" < "min_exp"]. `a_mantissa` and `b_mantissa` are fully randomized and such that their product, `c_exp` does not require a normalization shift. `a_sign` and `b_sign` are randomized such that for Test 7, stem:["a_sign" oplus "b_sign" = -1] and for Test 8, stem:["a_sign" oplus "b_sign" = +1] + +==== Test 9 +`a_exp` and `b_exp` are randomly generated such that stem:["a_exp" + "b_exp" \exists \in S, S = \{"min_exp",...,"min_exp + 5"}] +`a_mantissa` and `b_mantissa` are randomly generated such that their product does not require a normalization shift. +`a_sign` and `b_sign` generation are fully randomized. + +==== General Form +High precision to low precision floating point converts take an input operand, `hp` and output the result, `lp`. + +==== Determining hp_sign +The desired result's sign, `lp_sign` must match what is defined in <>. Performing a convert preserves the sign, so stem:["hp_sign" = "output_sign"]. + +==== Determining hp_exp +The desired result's exponent, `lp_exp` must match what is defined in <>. Performing a convert preserves the exponent as long as the exponent is normal before and after the convert or subnormal before and after (provided that both `hp` and `lp` share the same `min_exp`). Otherwise, if a transition from normal to subnormal is made, which occurs when stem:["hp_min_exp" < "hp_exp" < "lp_min_exp"], conversions limit the exponent to `lp_min_exp`. Therefore, stem:["hp_min_exp" = max("output_exp", "lp_min_exp")] + +==== Determining hp_mantissa +The desired result's mantissa, `lp_mantissa` must match what is defined in <>. As described in <>, `hp_exp` may represent a subnormal value in `hp`, meaning that the number of bits left to generate the desired mantissa pattern is not `m_bits`. The variable representing the actual number of bits left, `bits_left` is calculated as follows: stem:["bits_left" = "hp_m_bits" - max("hp_min_exp" - "input_exp", 0)]. Because it is impossible to go from subnormal in `hp` to normal in `lp`, I can fill the mantissa bits according to <>. If `hp_exp` is normal in `hp`, the first bit out of the LSB, rounding, and sticky bit will be satisfied after the conversion into `lp`. All bits of `hp_mantissa` will be determined based on the pattern of all following bits. + +=== Operations: Multiplication + +==== General Form +Multiplication of floating point numbers `a` and `b` to get `c` takes the following form: stem:[(1."mantissa_a") * (1."mantissa_b") * 2^("a_exp" + "b_exp") * -1^("a_sign" oplus "b_sign") = "c_mantissa" * 2^"c_exp" * -1^"c_sign"]. Each test has a different requirement on `c` which dictates the possible values for the input values `a` and `b`. + +==== Determining the input signs + +The desired value for `c_sign` matches the desired sign as stated in <>. If `c_sign`=0, `a_sign` is randomly generated and stem:["b_sign" = "a_sign"]. If `c_sign` =1, `a_sign` and `c_sign` are randomly generated and stem:["b_sign" = ("a_sign" + 1) % 2]. + +==== Determining the input exponents +The desired value for `c_exp` matches the exponent as stated in <>. The values `a_exp` and `b_exp` must satisfy the equation: stem:["a_exp"+"b_exp"="c_exp"]. To satisfy this equation, stem:["a_exp" = random("min_sn", "c_exp" - "min_sn")] such that the largest value `b_exp` can be is stem:["target" + "min_sn"]. Therefore, stem:["b_exp" = "c_exp" - "a_exp"]. + +==== Determining the input mantissas +The desired value for `c_mantissa` matches the mantissa as stated in <>. Choosing the appropriate values `a_mantissa` and `b_mantissa` will depend on `lrs_int`. If `lrs_int` = 1, `a_mantissa` and `b_mantissa` are randomly generated within in the range stem:[[1, 2^"bits_left")]. To ensure that `c_mantissa` < 2 and no normalization shift is required, `a_mantissa` and `b_mantissa` are regenerated until the result satisfies the inequality: stem:["a_mantissa" * "b_mantissa" < 2^("a_mantissa_bits" + "b_mantissa_bits" - 1)]. If `lrs_int` = 3, 5, or 7, mantissa generation occurs until the required equality on the lsb and rounding bits are satisfied. If `lrs_int` = 2 or 4, `a_mantissa` or `b_mantissa` is randomly assigned a value in the range stem:[2^("bits left" -1)]. If `lrs_int` = 6, `a_mantissa` or `b_mantissa` is randomly incremented by stem:[2^("bits_left" - 2)]. + +=== Operations: Division + +==== General Form + +Division of floating point numbers `a` and `b` resulting in the floating point value `c` take the following form: stem:["c_mantissa" * 2^"c_exp" * -1^"c_sign" = (1."mantissa_a") / (1."mantissa_b") * 2^("a_exp" - "b_exp") * -1^("a_sign" oplus "b_sign")]. + +==== Determining the input signs +Procedure identical to that listed in <>. + +==== Determining the input exponent +The desired value for `c_exp` matches the exponent as stated in <>. To avoid normalization after division, stem:["a_mantissa" > "b_mantissa"]. Because floating point adds a hidden 1 to normalized values, this means that a cannot be subnormal when `b` is normal. To avoid this condition, `a_exp` and `b_exp` are restricted to normal exponents. To satisfy the equation that stem:["c_exp" = "a_exp" - "b_exp"], the possible range of `a_exp` is first determined. stem:["a_exp" = "random"(max("min_sn", "min_sn" + "c_exp"), min("max_exp", "max_exp" + "c_exp"))]. With `a_exp` determined, stem:["b_exp" = "a_exp" - "c_exp"]. + +==== Determining the input mantissas +The desired value for `c_mantissa` matches the mantissa as stated in <>. If `lrs_int` = 2 or `lrs_int` = 4 the only way to satisfy the requirement on `c_mantissa` is to ensure that the first bit in the mantissa is a 1 and all lower bits are 0. If `lrs_int` = 1, then `a_mantissa` and `b_mantissa` = stem:["random" [1, 2^"m_bits"-1)]. If `lrs_int` = 6, then stem:["a_mantissa" = 2^("m_bits" - 1)] and the first bit in `b_mantissa` is a 1 with all lower bits being 0. If `lrs_int` = 5, then the following inequality must be satisfied: stem:[1 < "a"/"b" <3/2] If `lrs_int` = 7, then the following inequality must be satisfied: stem:[3/2<"a"/"b"<2]. Because `a` and `b` have the same range of possible values inequality around `a`/`b` greater than or less than 3/2 this test simply creates two values, a larger and a smaller value to represent the possible representations of `a`/`b`. The smaller value is within the range: stem:[(1, (2* ((2^"bits_left") -1))/3))]. Based on the smaller value, the large value lies within the range: stem:[(3/2* "small value", (2^"bits_left") - 1)]. If `lrs_int` = 5, `a_mantissa` = the smaller value and `b_mantissa` = the larger value. If `lrs_int` = 7, `a_mantissa` = the larger value and `b_mantissa` = the smaller value. + +=== Operations: All fused multiply operations +==== General Form +Floating point FMA operations of the operands `a1`, `a2`, and `b` take the form: + +* FMADD: stem:["c" = ("a1" * "a2") + "b"] +* FMSUB: stem:["c" = ("a1" * "a2") - "b"] +* FNMSUB: stem:["c" =-("a1" * "a2") + "b"] +* FNMADD: stem:["c" =-("a1" * "a2") - "b"] + +Each test has a different requirement on `c`, which determines the possible values for the inputs, `a1`, `a2`, and `b`. +==== Determining input signs +The value of `c_sign` matches the sign as stated in <>. Each FMA operation results in a different sign as a result of the multiplication and addition operation. Because the multiplication result can reach below `min_sn` but the addition operation is capped at `min_sn`, the sign of the addition dictates the sign of FMA operation. Therefore, based on the FMA operation being performed, the input sign of `b` is altered such that stem:["b_sign" = "c_sign"]. Because the smallest value of `b` is `min_sn`, and each test case seeks results below `min_sn`, the result of the multiplication must be the opposite of `b_sign`. Therefore, based on the FMA operation being performed, `a1_sign` and `a2_sign` are determined such that stem:[("a1_sign" oplus "a2_sign") oplus "multiplication sign" = ! "b_sign"]. Where multiplication sign represents 1 for FNMSUB and FNMADD and 0 for FMSUB and FMADD. + +==== Determining input exponent and mantissa + +The desired output sign and exponent, `c_sign` and `c_exp` match the values as stated in <> and <> respectively. Within tests 1 and 3 where the magnitude of the FMA operation lies between `minSubNorm` and `minSubNorm`/2, stem:["b" = "minSubNorm" * 2]. For tests 2 and 4 where the magnitude of the FMA operation lies between 0 and `minSubNorm`/2, stem:["b" = "minSubNorm"]. As observed in <>, each test has a different desired lsb, rounding bit, and sticky bit pattern, which can be represented as lrs_int. Since the value of `b` is determined, the bit pattern of `b` can also be represented as an integer. Regardless of the test, the integer representation of `b`, `b_int` is 8. The signs of each multiplication operation, as shown in <> are an effective subtraction from the addition operation. We can use the desired output `lrs_int` and `b_int` to determine the desired bit pattern of the multiplication operation, `mul_int`. Using the equation: stem:["b_int" - "lrs_int" = "mul_int"], the desired bit pattern of the multiplication is determined. This value `mul_int` is then passed into the multiplication procedure as `lrs_int` and the results are stored as `a1` and `a2`. + +== Test Count Breakdown +=== Number of Operations +[cols="4,1", options="header"] +|=== +| Operation Type | Count + +| One operand | 10 +| Two operands | 2 +| Three operands | 4 +| *Total* | *16* +|=== + +=== Configurations +[cols="4,1", options="header"] +|=== +| Parameter | Count + +| Number of precisions | 5 +| Rounding Modes | 5 +|=== + +=== Vectors Generated per Test +[cols="3*", options="header"] +|=== +| Test # | Condition | Count | + +1 | A random positive SubNorm | 1 | + +2 | A random negative SubNorm | 1 | + +3 | All numbers in the range [+ MinSubNorm - 3 ulp, + MinSubNorm + 3 ulp] | 7 | + +4 | All numbers in the range [- MinSubNorm - 3 ulp, - MinSubNorm + 3 ulp] | 7 | + +5 | All numbers in the range [ + MinNorm - 3 ulp, + MinNorm + 3 ulp] | 7 | + +6 | All numbers in the range [- MinNorm - 3 ulp, - MinNorm + 3 ulp] | 7 | + +7 | A random number in the range (0, MinSubNorm) | 1 | + +8 | A random numnber in the range (-MinSubNorm, -0) | 1 | + +9 | One number for every exponent in the range [MinNorm.exp, MinNorm.exp + 5] | 6 | + + +| *Total* | *38* +|=== + +=== Total Test Count +[cols="3*", options="header"] +|=== +| Scenario | Breakdown | Count +|Multiplication | (38 * 5 * 5) - (2 * 5) | 940 +|Division | (24 * 5 * 5) | 600 +|Converts | (10 * 5 * 38) | 1900 +|Add/Sub | (12 * 5 * 5) | 300 +|FMA | (38 * 5 * 5 * 4) | 3800 +| *Total Tests* | *7540* +|=== + +_*Differs from Aharoni test count_ diff --git a/docs/B6.adoc b/docs/B6.adoc index 81d4cab..d776bc5 100644 --- a/docs/B6.adoc +++ b/docs/B6.adoc @@ -28,7 +28,7 @@ Test 4):: stem:[+"MinSubNorm" / 2 < "intermediate" < +"MinSubNorm"] == Implementation === Definitions -`min_exp`:: The minimum exponent for each precision, where the biased exponent value is 1. +`min_exp`:: The minimum normal exponent for each precision, where the biased exponent value is 1. `lp/hp`:: As specified within the operations section, this test suite will be performing high to low precision converts. `hp` refers to the higher precision input operand for these operations, `lp` refers to the lower precision output of the conversion. `m_bits`:: The number of mantissa bits, dependent on the precision. `min_sn_exp`:: The smallest possible subnormal value based on the precision, 2^min_exp - m_bits. diff --git a/src/cover_float/testgen/B18.py b/src/cover_float/testgen/B18.py index ec3336c..e3652bc 100644 --- a/src/cover_float/testgen/B18.py +++ b/src/cover_float/testgen/B18.py @@ -95,13 +95,12 @@ def genUnderflowTests(test_f: TextIO, cover_f: TextIO) -> None: rounding_mode = random.choice(ROUNDING_MODES) for a, b in getMultiplyTests(precision, rounding_mode): for operation in FMA_OPS: - for sign in ["0", "1"]: - c = getRandomInt(min_exp, max_exp, sign, precision) - run_and_store_test_vector( - f"{operation}_{rounding_mode}_{a}_{b}_{c}_{precision}_{32 * '0'}_{precision}_00", - test_f, - cover_f, - ) + c = getRandomInt(min_exp, max_exp, str(random.randint(0, 1)), precision) + run_and_store_test_vector( + f"{operation}_{rounding_mode}_{a}_{b}_{c}_{precision}_{32 * '0'}_{precision}_00", + test_f, + cover_f, + ) # def genOverflowTests(test_f: TextIO, cover_f: TextIO) -> None: @@ -141,21 +140,21 @@ def get_fp_values(precision: str, grs_pattern: str, mul_sign: int, addend_sign: # Determine the input mantissas for each desired value target_list = { # Above MinNorm - "001": { + "001": { # Same as B5, FIX FMT_BF16: (8 + 1) * (8**2 - 8 + 1), FMT_HALF: ((2**4) + 1) * ((2**4) ** 2 - (2**4) + 1), FMT_SINGLE: ((2**5) + 1) * ((2**5) ** 4 - (2**5) ** 3 + (2**5) ** 2 - (2**5) + 1), FMT_DOUBLE: ((2**11) + 1) * ((2**11) ** 4 - (2**11) ** 3 + (2**11) ** 2 - (2**11) + 1), FMT_QUAD: ((2**38) + 1) * ((2**76) - (2**38) + 1), }, - "010": { + "010": { # Same as B5, FIX FMT_BF16: 2056, FMT_HALF: (2**11) + 1, FMT_SINGLE: ((2**8) + 1) * ((2**8) ** 2 - (2**8) + 1), FMT_DOUBLE: (2**53) + 1, FMT_QUAD: (2**113) + 1, }, - "011": { + "011": { # Same as B5, FIX FMT_BF16: 2**9 + 2 + 1, FMT_HALF: 2**13 + 4 + 3, FMT_SINGLE: (2**26) + 7, @@ -334,4 +333,4 @@ def get_fp_values(precision: str, grs_pattern: str, mul_sign: int, addend_sign: def main(test_f: TextIO, cover_f: TextIO) -> None: genUnderflowTests(test_f, cover_f) # overFlowTests(test_f, cover_f) - lsbGuardStickyTests(test_f, cover_f) + # lsbGuardStickyTests(test_f, cover_f) diff --git a/src/cover_float/testgen/B5.py b/src/cover_float/testgen/B5.py index 554292f..eb0d651 100644 --- a/src/cover_float/testgen/B5.py +++ b/src/cover_float/testgen/B5.py @@ -273,8 +273,6 @@ def convertTests(test_f: TextIO, cover_f: TextIO) -> None: hp = B5_FMTS[i_hp] for i_lp in range(i_hp + 1, len(B5_FMTS)): lp = B5_FMTS[i_lp] - # hp = B5_FMTS[0] - # lp = B5_FMTS[4] for rounding_mode in ROUNDING_MODES: tests_conversion_1_2(lp, hp, rounding_mode, test_f, cover_f) tests_conversion_3_4(lp, hp, rounding_mode, test_f, cover_f) @@ -461,7 +459,7 @@ def factor_mul_gen(precision: str, rounding_mode: str, grs_pattern: str, sign: s FMT_QUAD: ((2**38) + 1) * ((2**76) - (2**38) + 1), }, "010": { - FMT_BF16: 2056, + FMT_BF16: 1, FMT_HALF: (2**11) + 1, FMT_SINGLE: ((2**8) + 1) * ((2**8) ** 2 - (2**8) + 1), FMT_DOUBLE: (2**53) + 1, @@ -472,7 +470,7 @@ def factor_mul_gen(precision: str, rounding_mode: str, grs_pattern: str, sign: s FMT_HALF: 2**13 + 4 + 3, FMT_SINGLE: (2**26) + 7, FMT_DOUBLE: 2**55 + 7, - FMT_QUAD: 2**120 + 125, + FMT_QUAD: (2**112 + 2) * (2**113 - 2), }, # Below MinNorm "111": { @@ -507,8 +505,8 @@ def factor_mul_gen(precision: str, rounding_mode: str, grs_pattern: str, sign: s FMT_DOUBLE: ((2**11) + 1), FMT_QUAD: ((2**38) + 1), }, - "010": {FMT_BF16: 257, FMT_HALF: 683, FMT_SINGLE: ((2**8) + 1), FMT_DOUBLE: 321, FMT_QUAD: 491003369344660409}, - "011": {FMT_BF16: 103, FMT_HALF: 9, FMT_SINGLE: 23 * 29, FMT_DOUBLE: 9 * 25, FMT_QUAD: 1099511627781}, + "010": {FMT_BF16: 1, FMT_HALF: 683, FMT_SINGLE: ((2**8) + 1), FMT_DOUBLE: 321, FMT_QUAD: 491003369344660409}, + "011": {FMT_BF16: 103, FMT_HALF: 9, FMT_SINGLE: 23 * 29, FMT_DOUBLE: 9 * 25, FMT_QUAD: (2**112 + 2)}, # Below MinNorm "111": { FMT_BF16: (2**7) - 1, @@ -544,9 +542,6 @@ def factor_mul_gen(precision: str, rounding_mode: str, grs_pattern: str, sign: s factor_1 = factor_list[grs_pattern][precision] factor_2 = target_int // factor_1 # Integers have unlimited precision, must use integer division - if target_int == 1 and factor_1 == 1: - yield ("0", "0") - a_int = int(max(factor_1, factor_2)) # Ensure a is the largest value b_int = int(min(factor_1, factor_2)) @@ -555,6 +550,9 @@ def factor_mul_gen(precision: str, rounding_mode: str, grs_pattern: str, sign: s a_bits = len(a_bin) b_bits = len(b_bin) + # Thow error is bit width is impossibly long + if a_bits > m_bits + 1 or b_bits > m_bits + 1: + raise ValueError(f"a_bits ({a_bits}) or b_bits ({b_bits}) cannot be greater than m_bits + 1 ({m_bits + 1}).") exp_offset = a_bits - b_bits @@ -580,8 +578,8 @@ def factor_mul_gen(precision: str, rounding_mode: str, grs_pattern: str, sign: s a_fp = generate_FP(e_bits, str(a_sign), a_exp, a_bin, e_bias) b_fp = generate_FP(e_bits, str(b_sign), b_exp, b_bin, e_bias) - - yield (a_fp, b_fp) + if not (target_int == 1 and factor_1 == 1): + yield (a_fp, b_fp) def mul_div_grs_gen( @@ -690,7 +688,7 @@ def tests_multiply_5_6(precision: str, rounding_mode: str) -> Iterator[tuple[str # MinNorm + 2 ulp yield from factor_mul_gen(precision, rounding_mode, "010", "0") # Positive Test - yield from factor_mul_gen(precision, rounding_mode, "010", "1") # Negative Test #BF_16 is producing an error + yield from factor_mul_gen(precision, rounding_mode, "010", "1") # Negative Test BF_16 factor is impossible # MinNorm + 3 ulp yield from factor_mul_gen(precision, rounding_mode, "011", "0") # Positive Test @@ -721,17 +719,6 @@ def tests_multiply_9(precision: str, rounding_mode: str) -> Iterator[tuple[str, yield from mul_div_grs_gen(OP_MUL, precision, rounding_mode, "011", target_exp, sign, str(target_exp)) -# def multiplyTests(test_f: TextIO, cover_f: TextIO, genTests: bool) -> None: -# if genTests: -# for precision in FLOAT_FMTS: -# for rounding_mode in ROUNDING_MODES: -# tests_multiply_1_2(precision, rounding_mode, test_f, cover_f) -# tests_multiply_3_4(precision, rounding_mode, test_f, cover_f) -# tests_multiply_5_6(precision, rounding_mode, test_f, cover_f) -# tests_multiply_7_8(precision, rounding_mode, test_f, cover_f) -# tests_multiply_9(precision, rounding_mode, test_f, cover_f) - - def getMultiplyTests(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]]: yield from tests_multiply_1_2(precision, rounding_mode) yield from tests_multiply_3_4(precision, rounding_mode) @@ -1105,7 +1092,7 @@ def getDivTests(precision: str, rounding_mode: str) -> Iterator[tuple[str, str]] yield from tests_div_1_2(precision, rounding_mode) yield from tests_div_3_4(precision, rounding_mode) yield from tests_div_7_8(precision, rounding_mode) - # yield from tests_div_9(precision, rounding_mode) + yield from tests_div_9(precision, rounding_mode) def divTests(test_f: TextIO, cover_f: TextIO) -> None: @@ -1258,16 +1245,16 @@ def tests_add_sub_9(precision: str, rounding_mode: str, test_f: TextIO, cover_f: def addSubTests(test_f: TextIO, cover_f: TextIO) -> None: for precision in FLOAT_FMTS: for rounding_mode in ROUNDING_MODES: - tests_add_sub_1_2(precision, rounding_mode, test_f, cover_f) - tests_add_sub_3_4(precision, rounding_mode, test_f, cover_f) + # tests_add_sub_1_2(precision, rounding_mode, test_f, cover_f) + # tests_add_sub_3_4(precision, rounding_mode, test_f, cover_f) tests_add_sub_5_6(precision, rounding_mode, test_f, cover_f) - tests_add_sub_9(precision, rounding_mode, test_f, cover_f) + # tests_add_sub_9(precision, rounding_mode, test_f, cover_f) @register_model("B5") def main(test_f: TextIO, cover_f: TextIO) -> None: - convertTests(test_f, cover_f) + convertTests(test_f, cover_f) # Generating Too little tests multiplyTests(test_f, cover_f) - addSubTests(test_f, cover_f) + addSubTests(test_f, cover_f) # Generating too many tests fmaTests(test_f, cover_f) divTests(test_f, cover_f)