def standard_test_cases(self): fp64_list = [ # random test (sollya.parse("0x1.e906cc97d7cc1p+743"), sollya.parse("0x0.000001b84ba98p-1022")), (sollya.parse("0x1.9c4110b0dea4fp+279"), sollya.parse("0x0.000ccf2945bd8p-1022")), # OpenCL CTS error # infinite loop # ERROR: remquoD: {-inf, 77} ulp error at {0x0.eaffffffffb86p-1022, -0x0.0000000000202p-1022} ({ 0x000eaffffffffb86, 0x8000000000000202}): *{0x0.00000000000eap-1022, -78} ({ 0x00000000000000ea, 0xffffffb2}) vs. {-0x1.0000000000000p+0, -1} ({ 0xbff0000000000000, 0xffffffff}) (sollya.parse("0x0.eaffffffffb86p-1022"), sollya.parse("-0x0.0000000000202p-1022"), sollya.parse("0x0.00000000000eap-1022") if self.mode is REMAINDER_MODE else -78), # ERROR: remquoD: {0.000000, 92} ulp error at {-0x1.b9000000003e0p-982, -0x0.0000000000232p-1022} ({ 0x829b9000000003e0, 0x8000000000000232}): *{-0x0.0000000000000p+0, 0} ({ 0x8000000000000000, 0x00000000}) vs. {-0x0.0000000000000p+0, 92} ({ 0x8000000000000000, 0x0000005c}) (sollya.parse("-0x1.b9000000003e0p-982"), sollya.parse("-0x0.0000000000232p-1022"), FP_MinusZero(self.precision) if self.mode is REMAINDER_MODE else 0), # ERROR: remquoD: {-26458647810801664.000000, 1} ulp error at {-0x1.be000000005dfp+977, 0x1.78000000006f1p+975} ({ 0xfd0be000000005df, 0x7ce78000000006f1}): *{0x1.8000000002ce4p+973, -5} ({ 0x7cc8000000002ce4, 0xfffffffb}) vs. {-0x1.17ffffffffbb8p+975, -4} ({ 0xfce17ffffffffbb8, 0xfffffffc}) (sollya.parse("-0x1.be000000005dfp+977"), sollya.parse("0x1.78000000006f1p+975"), sollya.parse("0x1.8000000002ce4p+973") if self.mode is REMAINDER_MODE else -5), # ERROR: remquoD: {-171.000000, -1} ulp error at {-0x1.02ffffffffc2bp+489, -0x0.00000000000abp-1022} ({ 0xde802ffffffffc2b, 0x80000000000000ab}): *{0x0.0000000000055p-1022, 127} ({ 0x0000000000000055, 0x0000007f}) vs. {-0x0.0000000000056p-1022, 254} ({ 0x8000000000000056, 0x000000fe}) (sollya.parse("0x1.02ffffffffc2bp+489"), sollya.parse("0x0.00000000000abp-1022")), (sollya.parse("-0x1.02ffffffffc2bp+489"), sollya.parse("-0x0.00000000000abp-1022"), sollya.parse("0x0.0000000000055p-1022") if self.mode is REMAINDER_MODE else 127), # ERROR: remquoD: {nan, 0} ulp error at {-0x1.69000000001e1p+749, -inf} ({ 0xeec69000000001e1, 0xfff0000000000000}): *{-0x1.69000000001e1p+749, 0} ({ 0xeec69000000001e1, 0x00000000}) vs. {nan, 0} ({ 0x7ff8000000000000, 0x00000000}) (sollya.parse("-0x1.69000000001e1p+749"), FP_MinusInfty(self.precision), sollya.parse("-0x1.69000000001e1p+749") if self.mode is REMAINDER_MODE else 0), # ERROR: remquo: {0.000000, -126} ulp error at {0x1.921456p+70, -0x1.921456p+70} ({0x62c90a2b, 0xe2c90a2b}): *{0x0p+0, -1} ({0x00000000, 0xffffffff}) vs. {0x0p+0, 1} ({0x00000000, 0x00000001}) (sollya.parse("0x1.921456p+70"), sollya.parse("-0x1.921456p+70"), 0 if self.mode is REMAINDER_MODE else -1), # ERROR: remquoD: {10731233487093760.000000, 0} ulp error at {0x1.30fffffffff7ep-597, -0x1.13ffffffffed0p-596} ({ 0x1aa30fffffffff7e, 0x9ab13ffffffffed0}): *{-0x1.edffffffffc44p-598, -1} ({ 0x9a9edffffffffc44, 0xffffffff}) vs. {0x1.d000000000ae0p-600, -1} ({ 0x1a7d000000000ae0, 0xffffffff}) (sollya.parse("0x1.30fffffffff7ep-597"), sollya.parse("-0x1.13ffffffffed0p-596"), -1 if self.mode is QUOTIENT_MODE else sollya.parse("-0x1.edffffffffc44p-598")), # {-0x1.9bffffffffd38p+361, 0x0.00000000000a5p-1022} ({ 0xd689bffffffffd38, 0x00000000000000a5}): *{-0x0.000000000000fp-1022, -93} ({ 0x800000000000000f, 0xffffffa3}) vs. {0x0.000000000000fp-1022, -1171354717} ({ 0x000000000000000f, 0xba2e8ba3}) (sollya.parse("-0x1.9bffffffffd38p+361"), sollya.parse("0x0.00000000000a5p-1022")), (sollya.parse("0x0.ac0f94b9da13p-1022"), sollya.parse("-0x1.1e4580d7eb2e7p-1022")), (sollya.parse("0x1.fffffffffffffp+1023"), sollya.parse("-0x1.fffffffffffffp+1023")), (sollya.parse("0x0.a9f466178b1fcp-1022"), sollya.parse("0x0.b22f552dc829ap-1022")), (sollya.parse("0x1.4af8b07942537p-430"), sollya.parse("-0x0.f72be041645b7p-1022")), #result is 0x0.0000000000505p-1022 vs expected0x0.0000000000a3cp-1022 #(sollya.parse("0x1.9f9f4e9a29fcfp-421"), sollya.parse("0x0.0000000001b59p-1022"), sollya.parse("0x0.0000000000a3cp-1022")), (sollya.parse("0x1.9906165fb3e61p+62"), sollya.parse("0x1.9906165fb3e61p+60")), (sollya.parse("0x1.9906165fb3e61p+62"), sollya.parse("0x0.0000000005e7dp-1022")), (sollya.parse("0x1.77f00143ba3f4p+943"), sollya.parse("0x0.000000000001p-1022")), (sollya.parse("0x0.000000000001p-1022"), sollya.parse("0x0.000000000001p-1022")), (sollya.parse("0x0.000000000348bp-1022"), sollya.parse("0x0.000000000001p-1022")), (sollya.parse("0x1.bcf3955c3b244p-130"), sollya.parse("0x1.77aef33890951p-1003")), (sollya.parse("0x1.8de59bd84c51ep-866"), sollya.parse("0x1.045aa9bf14fb1p-774")), (sollya.parse("0x1.9f9f4e9a29fcfp-421"), sollya.parse("0x0.0000000001b59p-1022")), (sollya.parse("0x1.2e1c59b43a459p+953"), sollya.parse("0x0.0000001cf5319p-1022")), (sollya.parse("-0x1.86c83abe0854ep+268"), FP_MinusInfty(self.precision)), # bad sign of remainder (sollya.parse("0x1.d3fb9968850a5p-960"), sollya.parse("-0x0.23c1ed19c45fp-1022")), # bad sign of zero (FP_MinusZero(self.precision), sollya.parse("0x1.85200a9235193p-450")), # bad remainder (sollya.parse("0x1.fffffffffffffp+1023"), sollya.parse("0x1.1f31bcd002a7ap-803")), # bad sign (sollya.parse("-0x1.4607d0c9fc1a7p-878"), sollya.parse("-0x1.9b666b840b1bp-1023")), ] return fp64_list if self.precision.get_bit_size() >= 64 else []
def __call__(args): PRECISION = ML_Binary64 value_list = [ FP_PlusInfty(PRECISION), FP_MinusInfty(PRECISION), FP_PlusZero(PRECISION), FP_MinusZero(PRECISION), FP_QNaN(PRECISION), FP_SNaN(PRECISION), #FP_PlusOmega(PRECISION), #FP_MinusOmega(PRECISION), NumericValue(7.0), NumericValue(-3.0), ] op_map = { "+": operator.__add__, "-": operator.__sub__, "*": operator.__mul__, } for op in op_map: for lhs in value_list: for rhs in value_list: print("{} {} {} = {}".format(lhs, op, rhs, op_map[op](lhs, rhs))) return True
def get_integer_coding(self, value, language=C_Code): if FP_SpecialValue.is_special_value(value): return self.get_special_value_coding(value, language) elif value == ml_infty: return self.get_special_value_coding(FP_PlusInfty(self), language) elif value == -ml_infty: return self.get_special_value_coding(FP_MinusInfty(self), language) else: value = sollya.round(value, self.get_sollya_object(), sollya.RN) # FIXME: managing negative zero sign = int(1 if value < 0 else 0) value = abs(value) if value == 0.0: Log.report(Log.Warning, "+0.0 forced during get_integer_coding conversion") exp_biased = 0 mant = 0 else: exp = int(sollya.floor(sollya.log2(value))) exp_biased = int(exp - self.get_bias()) if exp < self.get_emin_normal(): exp_biased = 0 mant = int((value / S2**self.get_emin_subnormal())) else: mant = int( (value / S2**exp - 1.0) * (S2**self.get_field_size())) return mant | (exp_biased << self.get_field_size()) | ( sign << (self.get_field_size() + self.get_exponent_size()))
def __call__(args): for PRECISION in [ML_Binary32, ML_Binary64]: TEST_CASE = [ (FP_PlusOmega(PRECISION), "<", FP_SNaN(PRECISION), Unordered), (FP_PlusOmega(PRECISION), "<=", FP_SNaN(PRECISION), Unordered), (FP_PlusInfty(PRECISION), "==", FP_PlusInfty(PRECISION), True), (FP_PlusInfty(PRECISION), "==", FP_QNaN(PRECISION), False), (FP_PlusZero(PRECISION), "==", FP_MinusZero(PRECISION), True), (FP_PlusZero(PRECISION), "!=", FP_MinusZero(PRECISION), False), (FP_QNaN(PRECISION), "==", FP_QNaN(PRECISION), False), (FP_PlusInfty(PRECISION), "==", -FP_MinusInfty(PRECISION), True), (FP_MinusInfty(PRECISION), ">", 0, False), (FP_MinusInfty(PRECISION), ">", FP_PlusZero(PRECISION), False), (FP_MinusInfty(PRECISION), ">", FP_MinusZero(PRECISION), False), ] for lhs, op, rhs, expected in TEST_CASE: result = op_map[op](lhs, rhs) assert result == expected, "failure: {} {} {} = {} vs expected {} ".format(lhs, op, rhsresult, expected) return True
def numeric_emulate(self, vx, n): """ Numeric emulation of n-th root """ if FP_SpecialValue.is_special_value(vx): if is_nan(vx): return FP_QNaN(self.precision) elif is_plus_infty(vx): return SOLLYA_INFTY elif is_minus_infty(vx): if int(n) % 2 == 1: return vx else: return FP_QNaN(self.precision) elif is_zero(vx): if int(n) % 2 != 0 and n < 0: if is_plus_zero(vx): return FP_PlusInfty(self.precision) else: return FP_MinusInfty(self.precision) elif int(n) % 2 == 0: if n < 0: return FP_PlusInfty(self.precision) elif n > 0: return FP_PlusZero(self.precision) return FP_QNaN(self.precision) else: raise NotImplementedError # OpenCL-C rootn, x < 0 and y odd: -exp2(log2(-x) / y) S2 = sollya.SollyaObject(2) if vx < 0: if int(n) % 2 != 0: if n > 0: v = -bigfloat.root( sollya.SollyaObject(-vx).bigfloat(), int(n)) else: v = -S2**(sollya.log2(-vx) / n) else: return FP_QNaN(self.precision) elif n < 0: # OpenCL-C definition v = S2**(sollya.log2(vx) / n) else: v = bigfloat.root(sollya.SollyaObject(vx).bigfloat(), int(n)) return sollya.SollyaObject(v)
def generate_scheme(self): vx = self.implementation.add_input_variable("x", self.get_input_precision()) sollya_precision = self.get_input_precision().get_sollya_object() # local overloading of RaiseReturn operation def ExpRaiseReturn(*args, **kwords): kwords["arg_value"] = vx kwords["function_name"] = self.function_name return RaiseReturn(*args, **kwords) # testing special value inputs test_nan_or_inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, debug=True, tag="nan_or_inf") test_nan = Test(vx, specifier=Test.IsNaN, debug=True, tag="is_nan_test") test_positive = Comparison(vx, 0, specifier=Comparison.GreaterOrEqual, debug=True, tag="inf_sign") test_signaling_nan = Test(vx, specifier=Test.IsSignalingNaN, debug=True, tag="is_signaling_nan") # if input is a signaling NaN, raise an invalid exception and returns # a quiet NaN return_snan = Statement( ExpRaiseReturn(ML_FPE_Invalid, return_value=FP_QNaN(self.precision))) vx_exp = ExponentExtraction(vx, tag="vx_exp", debug=debugd) int_precision = self.precision.get_integer_format() # log2(vx) # r = vx_mant # e = vx_exp # vx reduced to r in [1, 2[ # log2(vx) = log2(r * 2^e) # = log2(r) + e # ## log2(r) is approximated by # log2(r) = log2(inv_seed(r) * r / inv_seed(r) # = log2(inv_seed(r) * r) - log2(inv_seed(r)) # inv_seed(r) in ]1/2, 1] => log2(inv_seed(r)) in ]-1, 0] # # inv_seed(r) * r ~ 1 # we can easily tabulate -log2(inv_seed(r)) # # retrieving processor inverse approximation table dummy_var = Variable("dummy", precision=self.precision) dummy_div_seed = DivisionSeed(dummy_var, precision=self.precision) inv_approx_table = self.processor.get_recursive_implementation( dummy_div_seed, language=None, table_getter=lambda self: self.approx_table_map) # table creation table_index_size = 7 log_table = ML_NewTable(dimensions=[2**table_index_size, 2], storage_precision=self.precision, tag=self.uniquify_name("inv_table")) # value for index 0 is set to 0.0 log_table[0][0] = 0.0 log_table[0][1] = 0.0 for i in range(1, 2**table_index_size): #inv_value = (1.0 + (self.processor.inv_approx_table[i] / S2**9) + S2**-52) * S2**-1 #inv_value = (1.0 + (inv_approx_table[i][0] / S2**9) ) * S2**-1 #print inv_approx_table[i][0], inv_value inv_value = inv_approx_table[i][0] value_high_bitsize = self.precision.get_field_size() - ( self.precision.get_exponent_size() + 1) value_high = round(log2(inv_value), value_high_bitsize, sollya.RN) value_low = round( log2(inv_value) - value_high, sollya_precision, sollya.RN) log_table[i][0] = value_high log_table[i][1] = value_low def compute_log(_vx, exp_corr_factor=None): _vx_mant = MantissaExtraction(_vx, tag="_vx_mant", precision=self.precision, debug=debug_lftolx) _vx_exp = ExponentExtraction(_vx, tag="_vx_exp", debug=debugd) # The main table is indexed by the 7 most significant bits # of the mantissa table_index = inv_approx_table.index_function(_vx_mant) table_index.set_attributes(tag="table_index", debug=debuglld) # argument reduction # Using AND -2 to exclude LSB set to 1 for Newton-Raphson convergence # TODO: detect if single operand inverse seed is supported by the targeted architecture pre_arg_red_index = TypeCast(BitLogicAnd( TypeCast(DivisionSeed(_vx_mant, precision=self.precision, tag="seed", debug=debug_lftolx, silent=True), precision=ML_UInt64), Constant(-2, precision=ML_UInt64), precision=ML_UInt64), precision=self.precision, tag="pre_arg_red_index", debug=debug_lftolx) arg_red_index = Select(Equal(table_index, 0), 1.0, pre_arg_red_index, tag="arg_red_index", debug=debug_lftolx) _red_vx = FMA(arg_red_index, _vx_mant, -1.0) _red_vx.set_attributes(tag="_red_vx", debug=debug_lftolx) inv_err = S2**-inv_approx_table.index_size red_interval = Interval(1 - inv_err, 1 + inv_err) # return in case of standard (non-special) input _log_inv_lo = TableLoad(log_table, table_index, 1, tag="log_inv_lo", debug=debug_lftolx) _log_inv_hi = TableLoad(log_table, table_index, 0, tag="log_inv_hi", debug=debug_lftolx) Log.report(Log.Verbose, "building mathematical polynomial") approx_interval = Interval(-inv_err, inv_err) poly_degree = sup( guessdegree( log2(1 + sollya.x) / sollya.x, approx_interval, S2** -(self.precision.get_field_size() * 1.1))) + 1 sollya.settings.display = sollya.hexadecimal global_poly_object, approx_error = Polynomial.build_from_approximation_with_error( log2(1 + sollya.x) / sollya.x, poly_degree, [self.precision] * (poly_degree + 1), approx_interval, sollya.absolute, error_function=lambda p, f, ai, mod, t: sollya.dirtyinfnorm( p - f, ai)) Log.report( Log.Info, "poly_degree={}, approx_error={}".format( poly_degree, approx_error)) poly_object = global_poly_object.sub_poly(start_index=1, offset=1) #poly_object = global_poly_object.sub_poly(start_index=0,offset=0) Attributes.set_default_silent(True) Attributes.set_default_rounding_mode(ML_RoundToNearest) Log.report(Log.Verbose, "generating polynomial evaluation scheme") pre_poly = PolynomialSchemeEvaluator.generate_horner_scheme( poly_object, _red_vx, unified_precision=self.precision) _poly = FMA(pre_poly, _red_vx, global_poly_object.get_cst_coeff(0, self.precision)) _poly.set_attributes(tag="poly", debug=debug_lftolx) Log.report( Log.Verbose, "sollya global_poly_object: {}".format( global_poly_object.get_sollya_object())) Log.report( Log.Verbose, "sollya poly_object: {}".format( poly_object.get_sollya_object())) corr_exp = _vx_exp if exp_corr_factor == None else _vx_exp + exp_corr_factor Attributes.unset_default_rounding_mode() Attributes.unset_default_silent() pre_result = -_log_inv_hi + (_red_vx * _poly + (-_log_inv_lo)) pre_result.set_attributes(tag="pre_result", debug=debug_lftolx) exact_log2_hi_exp = Conversion(corr_exp, precision=self.precision) exact_log2_hi_exp.set_attributes(tag="exact_log2_hi_hex", debug=debug_lftolx) _result = exact_log2_hi_exp + pre_result return _result, _poly, _log_inv_lo, _log_inv_hi, _red_vx result, poly, log_inv_lo, log_inv_hi, red_vx = compute_log(vx) result.set_attributes(tag="result", debug=debug_lftolx) # specific input value predicate neg_input = Comparison(vx, 0, likely=False, specifier=Comparison.Less, debug=debugd, tag="neg_input") vx_nan_or_inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, debug=debugd, tag="nan_or_inf") vx_snan = Test(vx, specifier=Test.IsSignalingNaN, likely=False, debug=debugd, tag="vx_snan") vx_inf = Test(vx, specifier=Test.IsInfty, likely=False, debug=debugd, tag="vx_inf") vx_subnormal = Test(vx, specifier=Test.IsSubnormal, likely=False, debug=debugd, tag="vx_subnormal") vx_zero = Test(vx, specifier=Test.IsZero, likely=False, debug=debugd, tag="vx_zero") exp_mone = Equal(vx_exp, -1, tag="exp_minus_one", debug=debugd, likely=False) vx_one = Equal(vx, 1.0, tag="vx_one", likely=False, debug=debugd) # Specific specific for the case exp == -1 # log2(x) = log2(m) - 1 # # as m in [1, 2[, log2(m) in [0, 1[ # if r is close to 2, a catastrophic cancellation can occur # # r = seed(m) # log2(x) = log2(seed(m) * m / seed(m)) - 1 # = log2(seed(m) * m) - log2(seed(m)) - 1 # # for m really close to 2 => seed(m) = 0.5 # => log2(x) = log2(0.5 * m) # = result_exp_m1 = (-log_inv_hi - 1.0) + FMA(poly, red_vx, -log_inv_lo) result_exp_m1.set_attributes(tag="result_exp_m1", debug=debug_lftolx) m100 = -100 S2100 = Constant(S2**100, precision=self.precision) result_subnormal, _, _, _, _ = compute_log(vx * S2100, exp_corr_factor=m100) result_subnormal.set_attributes(tag="result_subnormal", debug=debug_lftolx) one_err = S2**-7 approx_interval_one = Interval(-one_err, one_err) red_vx_one = vx - 1.0 poly_degree_one = sup( guessdegree( log(1 + x) / x, approx_interval_one, S2** -(self.precision.get_field_size() + 1))) + 1 poly_object_one = Polynomial.build_from_approximation( log(1 + sollya.x) / sollya.x, poly_degree_one, [self.precision] * (poly_degree_one + 1), approx_interval_one, absolute).sub_poly(start_index=1) poly_one = PolynomialSchemeEvaluator.generate_horner_scheme( poly_object_one, red_vx_one, unified_precision=self.precision) poly_one.set_attributes(tag="poly_one", debug=debug_lftolx) result_one = red_vx_one + red_vx_one * poly_one cond_one = (vx < (1 + one_err)) & (vx > (1 - one_err)) cond_one.set_attributes(tag="cond_one", debug=debugd, likely=False) # main scheme pre_scheme = ConditionBlock( neg_input, Statement(ClearException(), Raise(ML_FPE_Invalid), Return(FP_QNaN(self.precision))), ConditionBlock( vx_nan_or_inf, ConditionBlock( vx_inf, Statement( ClearException(), Return(FP_PlusInfty(self.precision)), ), Statement(ClearException(), ConditionBlock(vx_snan, Raise(ML_FPE_Invalid)), Return(FP_QNaN(self.precision)))), ConditionBlock( vx_subnormal, ConditionBlock( vx_zero, Statement( ClearException(), Raise(ML_FPE_DivideByZero), Return(FP_MinusInfty(self.precision)), ), Statement(ClearException(), result_subnormal, Return(result_subnormal))), ConditionBlock( vx_one, Statement( ClearException(), Return(FP_PlusZero(self.precision)), ), ConditionBlock(exp_mone, Return(result_exp_m1), Return(result)))))) scheme = Statement(result, pre_scheme) return scheme
def generate_scheme(self): vx = self.implementation.add_input_variable("x", self.precision) sollya_precision = self.precision.sollya_object # constant computation invlog2 = round(1 / log(2), sollya_precision, sollya.RN) invlog2_cst = Constant(invlog2, precision=self.precision) #v_log2_hi = round(log(2), 16, sollya.RN) #v_log2_lo = round(log(2) - v_log2_hi, sollya_precision, sollya.RN) #log2_hi = Constant(v_log2_hi, precision = self.precision, tag = "log2_hi") #log2_lo = Constant(v_log2_lo, precision = self.precision, tag = "log2_lo") # local overloading of RaiseReturn operation def ExpRaiseReturn(*args, **kwords): kwords["arg_value"] = vx kwords["function_name"] = self.function_name return RaiseReturn(*args, **kwords) test_nan_or_inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, debug=True, tag="nan_or_inf") test_nan = Test(vx, specifier=Test.IsNaN, debug=True, tag="is_nan_test") test_positive = Comparison(vx, 0, specifier=Comparison.GreaterOrEqual, debug=True, tag="inf_sign") test_signaling_nan = Test(vx, specifier=Test.IsSignalingNaN, debug=True, tag="is_signaling_nan") return_snan = Statement( ExpRaiseReturn(ML_FPE_Invalid, return_value=FP_QNaN(self.precision))) v_log2_hi = round( log(2), self.precision.get_field_size() - (self.precision.get_exponent_size() + 1), sollya.RN) v_log2_lo = round( log(2) - v_log2_hi, self.precision.sollya_object, sollya.RN) log2_hi = Constant(v_log2_hi, precision=self.precision, tag="log2_hi") log2_lo = Constant(v_log2_lo, precision=self.precision, tag="log2_lo") vx_exp = ExponentExtraction(vx, tag="vx_exp", debug=debug_multi) int_precision = self.precision.get_integer_format() # table creation table_index_size = 7 log_table = ML_NewTable(dimensions=[2**table_index_size, 2], storage_precision=self.precision, tag=self.uniquify_name("inv_table")) log_table[0][0] = 0.0 log_table[0][1] = 0.0 # retrieving processor inverse approximation table dummy_var = Variable("dummy", precision=self.precision) dummy_div_seed = ReciprocalSeed(dummy_var, precision=self.precision) inv_approx_table = self.processor.get_recursive_implementation( dummy_div_seed, language=None, table_getter=lambda self: self.approx_table_map) integer_precision = { ML_Binary32: ML_UInt32, ML_Binary64: ML_UInt64 }[self.precision] for i in range(1, 2**table_index_size): #inv_value = (1.0 + (self.processor.inv_approx_table[i] / S2**9) + S2**-52) * S2**-1 inv_value = inv_approx_table[ i] # (1.0 + (inv_approx_table[i][0] / S2**9) ) * S2**-1 value_high = round( log(inv_value), self.precision.get_field_size() - (self.precision.get_exponent_size() + 1), sollya.RN) value_low = round( log(inv_value) - value_high, sollya_precision, sollya.RN) log_table[i][0] = value_high log_table[i][1] = value_low def compute_log(_vx, exp_corr_factor=None): _vx_mant = MantissaExtraction(_vx, tag="_vx_mant", debug=debug_multi, precision=self.precision) _vx_exp = ExponentExtraction(_vx, tag="_vx_exp", debug=debug_multi) table_index = BitLogicAnd(BitLogicRightShift( TypeCast(_vx_mant, precision=int_precision, debug=debug_multi), self.precision.get_field_size() - 7, debug=debug_multi), 0x7f, tag="table_index", debug=debug_multi) # argument reduction # TODO: detect if single operand inverse seed is supported by the targeted architecture pre_arg_red_index = TypeCast(BitLogicAnd( TypeCast(ReciprocalSeed(_vx_mant, precision=self.precision, tag="seed", debug=debug_multi, silent=True), precision=integer_precision), Constant(-2, precision=integer_precision), precision=integer_precision), precision=self.precision, tag="pre_arg_red_index", debug=debug_multi) arg_red_index = Select(Equal(table_index, 0), 1.0, pre_arg_red_index) #_red_vx = arg_red_index * _vx_mant - 1.0 _red_vx = FusedMultiplyAdd(arg_red_index, _vx_mant, 1.0, specifier=FusedMultiplyAdd.Subtract) _red_vx.set_attributes(tag="_red_vx", debug=debug_multi) inv_err = S2**-7 red_interval = Interval(1 - inv_err, 1 + inv_err) # return in case of standard (non-special) input _log_inv_lo = TableLoad(log_table, table_index, 1, tag="log_inv_lo", debug=debug_multi) _log_inv_hi = TableLoad(log_table, table_index, 0, tag="log_inv_hi", debug=debug_multi) Log.report(Log.Verbose, "building mathematical polynomial") approx_interval = Interval(-inv_err, inv_err) poly_degree = sup( guessdegree( log(1 + sollya.x) / sollya.x, approx_interval, S2** -(self.precision.get_field_size() + 1))) + 1 global_poly_object = Polynomial.build_from_approximation( log(1 + sollya.x) / sollya.x, poly_degree, [1] + [self.precision] * (poly_degree), approx_interval, sollya.absolute) poly_object = global_poly_object.sub_poly(start_index=1) Log.report(Log.Verbose, "generating polynomial evaluation scheme") #_poly = PolynomialSchemeEvaluator.generate_horner_scheme(poly_object, _red_vx, unified_precision = self.precision) _poly = PolynomialSchemeEvaluator.generate_estrin_scheme( poly_object, _red_vx, unified_precision=self.precision) _poly.set_attributes(tag="poly", debug=debug_multi) corr_exp = Conversion( _vx_exp if exp_corr_factor == None else _vx_exp + exp_corr_factor, precision=self.precision) split_red_vx = Split(_red_vx, precision=ML_DoubleDouble, tag="split_red_vx", debug=debug_multi) red_vx_hi = split_red_vx.hi red_vx_lo = split_red_vx.lo # result = _red_vx * poly - log_inv_hi - log_inv_lo + _vx_exp * log2_hi + _vx_exp * log2_lo pre_result = -_log_inv_hi + (_red_vx + (_red_vx * _poly + (corr_exp * log2_lo - _log_inv_lo))) pre_result.set_attributes(tag="pre_result", debug=debug_multi) exact_log2_hi_exp = corr_exp * log2_hi exact_log2_hi_exp.set_attributes(tag="exact_log2_hi_exp", debug=debug_multi) cancel_part = (corr_exp * log2_hi - _log_inv_hi) cancel_part.set_attributes(tag="cancel_part", debug=debug_multi) sub_part = red_vx_hi + cancel_part sub_part.set_attributes(tag="sub_part", debug=debug_multi) #result_one_low_part = (red_vx_hi * _poly + (red_vx_lo + (red_vx_lo * _poly + (corr_exp * log2_lo - _log_inv_lo)))) result_one_low_part = ((red_vx_lo + (red_vx_lo * _poly + (corr_exp * log2_lo - _log_inv_lo)))) result_one_low_part.set_attributes(tag="result_one_low_part", debug=debug_multi) _result_one = ( (sub_part) + red_vx_hi * _poly) + result_one_low_part return exact_log2_hi_exp + pre_result, _poly, _log_inv_lo, _log_inv_hi, _red_vx, _result_one result, poly, log_inv_lo, log_inv_hi, red_vx, new_result_one = compute_log( vx) result.set_attributes(tag="result", debug=debug_multi) new_result_one.set_attributes(tag="new_result_one", debug=debug_multi) neg_input = Comparison(vx, 0, likely=False, specifier=Comparison.Less, debug=debug_multi, tag="neg_input") vx_nan_or_inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, debug=debug_multi, tag="nan_or_inf") vx_snan = Test(vx, specifier=Test.IsSignalingNaN, likely=False, debug=debug_multi, tag="snan") vx_inf = Test(vx, specifier=Test.IsInfty, likely=False, debug=debug_multi, tag="inf") vx_subnormal = Test(vx, specifier=Test.IsSubnormal, likely=False, debug=debug_multi, tag="vx_subnormal") vx_zero = Test(vx, specifier=Test.IsZero, likely=False, debug=debug_multi, tag="vx_zero") exp_mone = Equal(vx_exp, -1, tag="exp_minus_one", debug=debug_multi, likely=False) vx_one = Equal(vx, 1.0, tag="vx_one", likely=False, debug=debug_multi) # exp=-1 case Log.report(Log.Verbose, "managing exp=-1 case") result2 = (-log_inv_hi - log2_hi) + ( (red_vx + poly * red_vx) - log2_lo - log_inv_lo) result2.set_attributes(tag="result2", debug=debug_multi) m100 = -100 S2100 = Constant(S2**100, precision=self.precision) result_subnormal, _, _, _, _, _ = compute_log(vx * S2100, exp_corr_factor=m100) Log.report(Log.Verbose, "managing close to 1.0 cases") one_err = S2**-7 approx_interval_one = Interval(-one_err, one_err) red_vx_one = vx - 1.0 poly_degree_one = sup( guessdegree( log(1 + sollya.x) / sollya.x, approx_interval_one, S2** -(self.precision.get_field_size() + 1))) + 1 poly_object_one = Polynomial.build_from_approximation( log(1 + sollya.x) / sollya.x, poly_degree_one, [self.precision] * (poly_degree_one + 1), approx_interval_one, sollya.absolute).sub_poly(start_index=1) poly_one = PolynomialSchemeEvaluator.generate_horner_scheme( poly_object_one, red_vx_one, unified_precision=self.precision) poly_one.set_attributes(tag="poly_one", debug=debug_multi) result_one = red_vx_one + red_vx_one * poly_one cond_one = (vx < (1 + one_err)) & (vx > (1 - one_err)) cond_one.set_attributes(tag="cond_one", debug=debug_multi, likely=False) # main scheme pre_scheme = ConditionBlock( neg_input, Statement(ClearException(), Raise(ML_FPE_Invalid), Return(FP_QNaN(self.precision))), ConditionBlock( vx_nan_or_inf, ConditionBlock( vx_inf, Statement( ClearException(), Return(FP_PlusInfty(self.precision)), ), Statement(ClearException(), ConditionBlock(vx_snan, Raise(ML_FPE_Invalid)), Return(FP_QNaN(self.precision)))), ConditionBlock( vx_subnormal, ConditionBlock( vx_zero, Statement( ClearException(), Raise(ML_FPE_DivideByZero), Return(FP_MinusInfty(self.precision)), ), Return(result_subnormal)), ConditionBlock( vx_one, Statement( ClearException(), Return(FP_PlusZero(self.precision)), ), ConditionBlock(exp_mone, Return(result2), Return(result)) #ConditionBlock(cond_one, #Return(new_result_one), #ConditionBlock(exp_mone, #Return(result2), #Return(result) #) #) )))) scheme = pre_scheme return scheme
def generate_scalar_scheme(self, vx, vy): # fixing inputs' node tag vx.set_attributes(tag="x") vy.set_attributes(tag="y") int_precision = self.precision.get_integer_format() # assuming x = m.2^e (m in [1, 2[) # n, positive or null integers # # pow(x, n) = x^(y) # = exp(y * log(x)) # = 2^(y * log2(x)) # = 2^(y * (log2(m) + e)) # e = ExponentExtraction(vx, tag="e", precision=int_precision) m = MantissaExtraction(vx, tag="m", precision=self.precision) # approximation log2(m) # retrieving processor inverse approximation table dummy_var = Variable("dummy", precision = self.precision) dummy_div_seed = ReciprocalSeed(dummy_var, precision = self.precision) inv_approx_table = self.processor.get_recursive_implementation( dummy_div_seed, language=None, table_getter= lambda self: self.approx_table_map) log_f = sollya.log(sollya.x) # /sollya.log(self.basis) ml_log_args = ML_GenericLog.get_default_args(precision=self.precision, basis=2) ml_log = ML_GenericLog(ml_log_args) log_table, log_table_tho, table_index_range = ml_log.generate_log_table(log_f, inv_approx_table) log_approx = ml_log.generate_reduced_log_split(Abs(m, precision=self.precision), log_f, inv_approx_table, log_table) log_approx = Select(Equal(vx, 0), FP_MinusInfty(self.precision), log_approx) log_approx.set_attributes(tag="log_approx", debug=debug_multi) r = Multiplication(log_approx, vy, tag="r", debug=debug_multi) # 2^(y * (log2(m) + e)) = 2^(y * log2(m)) * 2^(y * e) # # log_approx = log2(Abs(m)) # r = y * log_approx ~ y * log2(m) # # NOTES: manage cases where e is negative and # (y * log2(m)) AND (y * e) could cancel out # if e positive, whichever the sign of y (y * log2(m)) and (y * e) CANNOT # be of opposite signs # log2(m) in [0, 1[ so cancellation can occur only if e == -1 # we split 2^x in 2^x = 2^t0 * 2^t1 # if e < 0: t0 = y * (log2(m) + e), t1=0 # else: t0 = y * log2(m), t1 = y * e t_cond = e < 0 # e_y ~ e * y e_f = Conversion(e, precision=self.precision) #t0 = Select(t_cond, (e_f + log_approx) * vy, Multiplication(e_f, vy), tag="t0") #NearestInteger(t0, precision=self.precision, tag="t0_int") EY = NearestInteger(e_f * vy, tag="EY", precision=self.precision) LY = NearestInteger(log_approx * vy, tag="LY", precision=self.precision) t0_int = Select(t_cond, EY + LY, EY, tag="t0_int") t0_frac = Select(t_cond, FMA(e_f, vy, -EY) + FMA(log_approx, vy, -LY) ,EY - t0_int, tag="t0_frac") #t0_frac.set_attributes(tag="t0_frac") ml_exp2_args = ML_Exp2.get_default_args(precision=self.precision) ml_exp2 = ML_Exp2(ml_exp2_args) exp2_t0_frac = ml_exp2.generate_scalar_scheme(t0_frac, inline_select=True) exp2_t0_frac.set_attributes(tag="exp2_t0_frac", debug=debug_multi) exp2_t0_int = ExponentInsertion(Conversion(t0_int, precision=int_precision), precision=self.precision, tag="exp2_t0_int") t1 = Select(t_cond, Constant(0, precision=self.precision), r) exp2_t1 = ml_exp2.generate_scalar_scheme(t1, inline_select=True) exp2_t1.set_attributes(tag="exp2_t1", debug=debug_multi) result_sign = Constant(1.0, precision=self.precision) # Select(n_is_odd, CopySign(vx, Constant(1.0, precision=self.precision)), 1) y_int = NearestInteger(vy, precision=self.precision) y_is_integer = Equal(y_int, vy) y_is_even = LogicalOr( # if y is a number (exc. inf) greater than 2**mantissa_size * 2, # then it is an integer multiple of 2 => even Abs(vy) >= 2**(self.precision.get_mantissa_size()+1), LogicalAnd( y_is_integer and Abs(vy) < 2**(self.precision.get_mantissa_size()+1), # we want to limit the modulo computation to an integer input Equal(Modulo(Conversion(y_int, precision=int_precision), 2), 0) ) ) y_is_odd = LogicalAnd( LogicalAnd( Abs(vy) < 2**(self.precision.get_mantissa_size()+1), y_is_integer ), Equal(Modulo(Conversion(y_int, precision=int_precision), 2), 1) ) # special cases management special_case_results = Statement( # x is sNaN OR y is sNaN ConditionBlock( LogicalOr(Test(vx, specifier=Test.IsSignalingNaN), Test(vy, specifier=Test.IsSignalingNaN)), Return(FP_QNaN(self.precision)) ), # pow(x, ±0) is 1 if x is not a signaling NaN ConditionBlock( Test(vy, specifier=Test.IsZero), Return(Constant(1.0, precision=self.precision)) ), # pow(±0, y) is ±∞ and signals the divideByZero exception for y an odd integer <0 ConditionBlock( LogicalAnd(Test(vx, specifier=Test.IsZero), LogicalAnd(y_is_odd, vy < 0)), Return(Select(Test(vx, specifier=Test.IsPositiveZero), FP_PlusInfty(self.precision), FP_MinusInfty(self.precision))), ), # pow(±0, −∞) is +∞ with no exception ConditionBlock( LogicalAnd(Test(vx, specifier=Test.IsZero), Test(vy, specifier=Test.IsNegativeInfty)), Return(FP_MinusInfty(self.precision)), ), # pow(±0, +∞) is +0 with no exception ConditionBlock( LogicalAnd(Test(vx, specifier=Test.IsZero), Test(vy, specifier=Test.IsPositiveInfty)), Return(FP_PlusInfty(self.precision)), ), # pow(±0, y) is ±0 for finite y>0 an odd integer ConditionBlock( LogicalAnd(Test(vx, specifier=Test.IsZero), LogicalAnd(y_is_odd, vy > 0)), Return(vx), ), # pow(−1, ±∞) is 1 with no exception ConditionBlock( LogicalAnd(Equal(vx, -1), Test(vy, specifier=Test.IsInfty)), Return(Constant(1.0, precision=self.precision)), ), # pow(+1, y) is 1 for any y (even a quiet NaN) ConditionBlock( vx == 1, Return(Constant(1.0, precision=self.precision)), ), # pow(x, +∞) is +0 for −1<x<1 ConditionBlock( LogicalAnd(Abs(vx) < 1, Test(vy, specifier=Test.IsPositiveInfty)), Return(FP_PlusZero(self.precision)) ), # pow(x, +∞) is +∞ for x<−1 or for 1<x (including ±∞) ConditionBlock( LogicalAnd(Abs(vx) > 1, Test(vy, specifier=Test.IsPositiveInfty)), Return(FP_PlusInfty(self.precision)) ), # pow(x, −∞) is +∞ for −1<x<1 ConditionBlock( LogicalAnd(Abs(vx) < 1, Test(vy, specifier=Test.IsNegativeInfty)), Return(FP_PlusInfty(self.precision)) ), # pow(x, −∞) is +0 for x<−1 or for 1<x (including ±∞) ConditionBlock( LogicalAnd(Abs(vx) > 1, Test(vy, specifier=Test.IsNegativeInfty)), Return(FP_PlusZero(self.precision)) ), # pow(+∞, y) is +0 for a number y < 0 ConditionBlock( LogicalAnd(Test(vx, specifier=Test.IsPositiveInfty), vy < 0), Return(FP_PlusZero(self.precision)) ), # pow(+∞, y) is +∞ for a number y > 0 ConditionBlock( LogicalAnd(Test(vx, specifier=Test.IsPositiveInfty), vy > 0), Return(FP_PlusInfty(self.precision)) ), # pow(−∞, y) is −0 for finite y < 0 an odd integer # TODO: check y is finite ConditionBlock( LogicalAnd(Test(vx, specifier=Test.IsNegativeInfty), LogicalAnd(y_is_odd, vy < 0)), Return(FP_MinusZero(self.precision)), ), # pow(−∞, y) is −∞ for finite y > 0 an odd integer # TODO: check y is finite ConditionBlock( LogicalAnd(Test(vx, specifier=Test.IsNegativeInfty), LogicalAnd(y_is_odd, vy > 0)), Return(FP_MinusInfty(self.precision)), ), # pow(−∞, y) is +0 for finite y < 0 and not an odd integer # TODO: check y is finite ConditionBlock( LogicalAnd(Test(vx, specifier=Test.IsNegativeInfty), LogicalAnd(LogicalNot(y_is_odd), vy < 0)), Return(FP_PlusZero(self.precision)), ), # pow(−∞, y) is +∞ for finite y > 0 and not an odd integer # TODO: check y is finite ConditionBlock( LogicalAnd(Test(vx, specifier=Test.IsNegativeInfty), LogicalAnd(LogicalNot(y_is_odd), vy > 0)), Return(FP_PlusInfty(self.precision)), ), # pow(±0, y) is +∞ and signals the divideByZero exception for finite y<0 and not an odd integer # TODO: signal divideByZero exception ConditionBlock( LogicalAnd(Test(vx, specifier=Test.IsZero), LogicalAnd(LogicalNot(y_is_odd), vy < 0)), Return(FP_PlusInfty(self.precision)), ), # pow(±0, y) is +0 for finite y>0 and not an odd integer ConditionBlock( LogicalAnd(Test(vx, specifier=Test.IsZero), LogicalAnd(LogicalNot(y_is_odd), vy > 0)), Return(FP_PlusZero(self.precision)), ), ) # manage n=1 separately to avoid catastrophic propagation of errors # between log2 and exp2 to eventually compute the identity function # test-case #3 result = Statement( special_case_results, # fallback default cases Return(result_sign * exp2_t1 * exp2_t0_int * exp2_t0_frac)) return result
def numeric_emulate(self, vx, vy): """ Numeric emulation of pow """ if is_snan(vx) or is_snan(vy): return FP_QNaN(self.precision) # pow(x, ±0) is 1 if x is not a signaling NaN if is_zero(vy): return 1.0 # pow(±0, y) is ±∞ and signals the divideByZero exception for y an odd integer <0 if is_plus_zero(vx) and not is_special_value(vy) and (int(vy) == vy) and (int(vy) % 2 == 1) and vy < 0: return FP_PlusInfty(self.precision) if is_minus_zero(vx) and not is_special_value(vy) and (int(vy) == vy) and (int(vy) % 2 == 1) and vy < 0: return FP_MinusInfty(self.precision) # pow(±0, −∞) is +∞ with no exception if is_zero(vx) and is_minus_zero(vy): return FP_MinusInfty(self.precision) # pow(±0, +∞) is +0 with no exception if is_zero(vx) and is_plus_zero(vy): return FP_PlusZero(self.precision) # pow(±0, y) is ±0 for finite y>0 an odd integer if is_zero(vx) and not is_special_value(vy) and (int(vy) == vy) and (int(vy) % 2 == 1) and vy > 0: return vx # pow(−1, ±∞) is 1 with no exception if vx == -1.0 and is_infty(vy): return 1 # pow(+1, y) is 1 for any y (even a quiet NaN) if vx == 1.0: return 1.0 # pow(x, +∞) is +0 for −1<x<1 if -1 < vx < 1 and is_plus_infty(vy): return FP_PlusZero(self.precision) # pow(x, +∞) is +∞ for x<−1 or for 1<x (including ±∞) if (vx < -1 or vx > 1) and is_plus_infty(vy): return FP_PlusInfty(self.precision) # pow(x, −∞) is +∞ for −1<x<1 if -1 < vx < 1 and is_minus_infty(vy): return FP_PlusInfty(self.precision) # pow(x, −∞) is +0 for x<−1 or for 1<x (including ±∞) if is_minus_infty(vy) and (vx < -1 or vx > 1): return FP_PlusZero(self.precision) # pow(+∞, y) is +0 for a number y < 0 if is_plus_infty(vx) and vy < 0: return FP_PlusZero(self.precision) # pow(+∞, y) is +∞ for a number y > 0 if is_plus_infty(vx) and vy > 0: return FP_PlusInfty(self.precision) # pow(−∞, y) is −0 for finite y < 0 an odd integer if is_minus_infty(vx) and vy < 0 and not is_special_value(vy) and int(vy) == vy and int(vy) % 2 == 1: return FP_MinusZero(self.precision) # pow(−∞, y) is −∞ for finite y > 0 an odd integer if is_minus_infty(vx) and vy > 0 and not is_special_value(vy) and int(vy) == vy and int(vy) % 2 == 1: return FP_MinusInfty(self.precision) # pow(−∞, y) is +0 for finite y < 0 and not an odd integer if is_minus_infty(vx) and vy < 0 and not is_special_value(vy) and not(int(vy) == vy and int(vy) % 2 == 1): return FP_PlusZero(self.precision) # pow(−∞, y) is +∞ for finite y > 0 and not an odd integer if is_minus_infty(vx) and vy > 0 and not is_special_value(vy) and not(int(vy) == vy and int(vy) % 2 == 1): return FP_PlusInfty(self.precision) # pow(±0, y) is +∞ and signals the divideByZero exception for finite y<0 and not an odd integer if is_zero(vx) and vy < 0 and not is_special_value(vy) and not(int(vy) == vy and int(vy) % 2 == 1): return FP_PlusInfty(self.precision) # pow(±0, y) is +0 for finite y>0 and not an odd integer if is_zero(vx) and vy > 0 and not is_special_value(vy) and not(int(vy) == vy and int(vy) % 2 == 1): return FP_PlusZero(self.precision) # TODO # pow(x, y) signals the invalid operation exception for finite x<0 and finite non-integer y. return sollya.SollyaObject(vx)**sollya.SollyaObject(vy)
def generate_scheme(self): # We wish to compute vx / vy vx = self.implementation.add_input_variable( "x", self.precision, interval=self.input_intervals[0]) vy = self.implementation.add_input_variable( "y", self.precision, interval=self.input_intervals[1]) # maximum exponent magnitude (to avoid overflow/ underflow during # intermediary computations int_prec = self.precision.get_integer_format() max_exp_mag = Constant(self.precision.get_emax() - 1, precision=int_prec) exact_ex = ExponentExtraction(vx, tag="exact_ex", precision=int_prec, debug=debug_multi) exact_ey = ExponentExtraction(vy, tag="exact_ey", precision=int_prec, debug=debug_multi) ex = Max(Min(exact_ex, max_exp_mag, precision=int_prec), -max_exp_mag, tag="ex", precision=int_prec) ey = Max(Min(exact_ey, max_exp_mag, precision=int_prec), -max_exp_mag, tag="ey", precision=int_prec) Attributes.set_default_rounding_mode(ML_RoundToNearest) Attributes.set_default_silent(True) # computing the inverse square root init_approx = None scaling_factor_x = ExponentInsertion(-ex, tag="sfx_ei", precision=self.precision, debug=debug_multi) scaling_factor_y = ExponentInsertion(-ey, tag="sfy_ei", precision=self.precision, debug=debug_multi) def test_interval_out_of_bound_risk(x_range, y_range): """ Try to determine from x and y's interval if there is a risk of underflow or overflow """ div_range = abs(x_range / y_range) underflow_risk = sollya.inf(div_range) < S2**( self.precision.get_emin_normal() + 2) overflow_risk = sollya.sup(div_range) > S2**( self.precision.get_emax() - 2) return underflow_risk or overflow_risk out_of_bound_risk = (self.input_intervals[0] is None or self.input_intervals[1] is None ) or test_interval_out_of_bound_risk( self.input_intervals[0], self.input_intervals[1]) Log.report(Log.Debug, "out_of_bound_risk: {}".format(out_of_bound_risk)) # scaled version of vx and vy, to avoid overflow and underflow if out_of_bound_risk: scaled_vx = vx * scaling_factor_x scaled_vy = vy * scaling_factor_y scaled_interval = MetaIntervalList( [MetaInterval(Interval(-2, -1)), MetaInterval(Interval(1, 2))]) scaled_vx.set_attributes(tag="scaled_vx", debug=debug_multi, interval=scaled_interval) scaled_vy.set_attributes(tag="scaled_vy", debug=debug_multi, interval=scaled_interval) seed_interval = 1 / scaled_interval print("seed_interval=1/{}={}".format(scaled_interval, seed_interval)) else: scaled_vx = vx scaled_vy = vy seed_interval = 1 / scaled_vy.get_interval() # We need a first approximation to 1 / scaled_vy dummy_seed = ReciprocalSeed(EmptyOperand(precision=self.precision), precision=self.precision) if self.processor.is_supported_operation(dummy_seed, self.language): init_approx = ReciprocalSeed(scaled_vy, precision=self.precision, tag="init_approx", debug=debug_multi) else: # generate tabulated version of seed raise NotImplementedError current_approx_std = init_approx # correctly-rounded inverse computation num_iteration = self.num_iter Attributes.unset_default_rounding_mode() Attributes.unset_default_silent() # check if inputs are zeros x_zero = Test(vx, specifier=Test.IsZero, likely=False, precision=ML_Bool) y_zero = Test(vy, specifier=Test.IsZero, likely=False, precision=ML_Bool) comp_sign = Test(vx, vy, specifier=Test.CompSign, tag="comp_sign", debug=debug_multi) # check if divisor is NaN y_nan = Test(vy, specifier=Test.IsNaN, likely=False, precision=ML_Bool) # check if inputs are signaling NaNs x_snan = Test(vx, specifier=Test.IsSignalingNaN, likely=False, precision=ML_Bool) y_snan = Test(vy, specifier=Test.IsSignalingNaN, likely=False, precision=ML_Bool) # check if inputs are infinities x_inf = Test(vx, specifier=Test.IsInfty, likely=False, tag="x_inf", precision=ML_Bool) y_inf = Test(vy, specifier=Test.IsInfty, likely=False, tag="y_inf", debug=debug_multi, precision=ML_Bool) scheme = None gappa_vx, gappa_vy = None, None # initial reciprocal approximation of 1.0 / scaled_vy inv_iteration_list, recp_approx = compute_reduced_reciprocal( init_approx, scaled_vy, self.num_iter) recp_approx.set_attributes(tag="recp_approx", debug=debug_multi) # approximation of scaled_vx / scaled_vy yerr_last, reduced_div_approx, div_iteration_list = compute_reduced_division( scaled_vx, scaled_vy, recp_approx) eval_error_range, div_eval_error_range = self.solve_eval_error( init_approx, recp_approx, reduced_div_approx, scaled_vx, scaled_vy, inv_iteration_list, div_iteration_list, S2**-7, seed_interval) eval_error = sup(abs(eval_error_range)) recp_interval = 1 / scaled_vy.get_interval() + eval_error_range recp_approx.set_interval(recp_interval) div_interval = scaled_vx.get_interval() / scaled_vy.get_interval( ) + div_eval_error_range reduced_div_approx.set_interval(div_interval) reduced_div_approx.set_tag("reduced_div_approx") if out_of_bound_risk: unscaled_result = scaling_div_result(reduced_div_approx, ex, scaling_factor_y, self.precision) subnormal_result = subnormalize_result(recp_approx, reduced_div_approx, ex, ey, yerr_last, self.precision) else: unscaled_result = reduced_div_approx subnormal_result = reduced_div_approx x_inf_or_nan = Test(vx, specifier=Test.IsInfOrNaN, likely=False) y_inf_or_nan = Test(vy, specifier=Test.IsInfOrNaN, likely=False, tag="y_inf_or_nan", debug=debug_multi) # generate IEEE exception raising only of libm-compliant # mode is enabled enable_raise = self.libm_compliant # managing special cases # x inf and y inf pre_scheme = ConditionBlock( x_inf_or_nan, ConditionBlock( x_inf, ConditionBlock( y_inf_or_nan, Statement( # signaling NaNs raise invalid operation flags ConditionBlock(y_snan, Raise(ML_FPE_Invalid)) if enable_raise else Statement(), Return(FP_QNaN(self.precision)), ), ConditionBlock(comp_sign, Return(FP_MinusInfty(self.precision)), Return(FP_PlusInfty(self.precision)))), Statement( ConditionBlock(x_snan, Raise(ML_FPE_Invalid)) if enable_raise else Statement(), Return(FP_QNaN(self.precision)))), ConditionBlock( x_zero, ConditionBlock( LogicalOr(y_zero, y_nan, precision=ML_Bool), Statement( ConditionBlock(y_snan, Raise(ML_FPE_Invalid)) if enable_raise else Statement(), Return(FP_QNaN(self.precision))), Return(vx)), ConditionBlock( y_inf_or_nan, ConditionBlock( y_inf, Return( Select(comp_sign, FP_MinusZero(self.precision), FP_PlusZero(self.precision))), Statement( ConditionBlock(y_snan, Raise(ML_FPE_Invalid)) if enable_raise else Statement(), Return(FP_QNaN(self.precision)))), ConditionBlock( y_zero, Statement( Raise(ML_FPE_DivideByZero) if enable_raise else Statement(), ConditionBlock( comp_sign, Return(FP_MinusInfty(self.precision)), Return(FP_PlusInfty(self.precision)))), # managing numerical value result cases Statement( recp_approx, reduced_div_approx, ConditionBlock( Test(unscaled_result, specifier=Test.IsSubnormal, likely=False), # result is subnormal Statement( # inexact flag should have been raised when computing yerr_last # ConditionBlock( # Comparison( # yerr_last, 0, # specifier=Comparison.NotEqual, likely=True), # Statement(Raise(ML_FPE_Inexact, ML_FPE_Underflow)) #), Return(subnormal_result), ), # result is normal Statement( # inexact flag should have been raised when computing yerr_last #ConditionBlock( # Comparison( # yerr_last, 0, # specifier=Comparison.NotEqual, likely=True), # Raise(ML_FPE_Inexact) #), Return(unscaled_result))), ))))) # managing rounding mode save and restore # to ensure intermediary computations are performed in round-to-nearest # clearing exception before final computation #rnd_mode = GetRndMode() #scheme = Statement( # rnd_mode, # SetRndMode(ML_RoundToNearest), # yerr_last, # SetRndMode(rnd_mode), # unscaled_result, # ClearException(), # pre_scheme #) scheme = pre_scheme return scheme
def generate_scalar_scheme(self, vx, n): # fixing inputs' node tag vx.set_attributes(tag="x") n.set_attributes(tag="n") int_precision = self.precision.get_integer_format() # assuming x = m.2^e (m in [1, 2[) # n, positive or null integers # # rootn(x, n) = x^(1/n) # = exp(1/n * log(x)) # = 2^(1/n * log2(x)) # = 2^(1/n * (log2(m) + e)) # # approximation log2(m) # retrieving processor inverse approximation table dummy_var = Variable("dummy", precision=self.precision) dummy_div_seed = ReciprocalSeed(dummy_var, precision=self.precision) inv_approx_table = self.processor.get_recursive_implementation( dummy_div_seed, language=None, table_getter=lambda self: self.approx_table_map) log_f = sollya.log(sollya.x) # /sollya.log(self.basis) use_reciprocal = False # non-scaled vx used to compute vx^1 unmodified_vx = vx is_subnormal = Test(vx, specifier=Test.IsSubnormal, tag="is_subnormal") exp_correction_factor = self.precision.get_mantissa_size() mantissa_factor = Constant(2**exp_correction_factor, tag="mantissa_factor") vx = Select(is_subnormal, vx * mantissa_factor, vx, tag="corrected_vx") m = MantissaExtraction(vx, tag="m", precision=self.precision) e = ExponentExtraction(vx, tag="e", precision=int_precision) e = Select(is_subnormal, e - exp_correction_factor, e, tag="corrected_e") ml_log_args = ML_GenericLog.get_default_args(precision=self.precision, basis=2) ml_log = ML_GenericLog(ml_log_args) log_table, log_table_tho, table_index_range = ml_log.generate_log_table( log_f, inv_approx_table) log_approx = ml_log.generate_reduced_log_split( Abs(m, precision=self.precision), log_f, inv_approx_table, log_table) # floating-point version of n n_f = Conversion(n, precision=self.precision, tag="n_f") inv_n = Division(Constant(1, precision=self.precision), n_f) log_approx = Select(Equal(vx, 0), FP_MinusInfty(self.precision), log_approx) log_approx.set_attributes(tag="log_approx", debug=debug_multi) if use_reciprocal: r = Multiplication(log_approx, inv_n, tag="r", debug=debug_multi) else: r = Division(log_approx, n_f, tag="r", debug=debug_multi) # e_n ~ e / n e_f = Conversion(e, precision=self.precision, tag="e_f") if use_reciprocal: e_n = Multiplication(e_f, inv_n, tag="e_n") else: e_n = Division(e_f, n_f, tag="e_n") error_e_n = FMA(e_n, -n_f, e_f, tag="error_e_n") e_n_int = NearestInteger(e_n, precision=self.precision, tag="e_n_int") pre_e_n_frac = e_n - e_n_int pre_e_n_frac.set_attributes(tag="pre_e_n_frac") e_n_frac = pre_e_n_frac + error_e_n * inv_n e_n_frac.set_attributes(tag="e_n_frac") ml_exp2_args = ML_Exp2.get_default_args(precision=self.precision) ml_exp2 = ML_Exp2(ml_exp2_args) exp2_r = ml_exp2.generate_scalar_scheme(r, inline_select=True) exp2_r.set_attributes(tag="exp2_r", debug=debug_multi) exp2_e_n_frac = ml_exp2.generate_scalar_scheme(e_n_frac, inline_select=True) exp2_e_n_frac.set_attributes(tag="exp2_e_n_frac", debug=debug_multi) exp2_e_n_int = ExponentInsertion(Conversion(e_n_int, precision=int_precision), precision=self.precision, tag="exp2_e_n_int") n_is_even = Equal(Modulo(n, 2), 0, tag="n_is_even", debug=debug_multi) n_is_odd = LogicalNot(n_is_even, tag="n_is_odd") result_sign = Select( n_is_odd, CopySign(vx, Constant(1.0, precision=self.precision)), 1) # managing n == -1 if self.expand_div: ml_division_args = ML_Division.get_default_args( precision=self.precision, input_formats=[self.precision] * 2) ml_division = ML_Division(ml_division_args) self.division_implementation = ml_division.implementation self.division_implementation.set_scheme( ml_division.generate_scheme()) ml_division_fct = self.division_implementation.get_function_object( ) else: ml_division_fct = Division # manage n=1 separately to avoid catastrophic propagation of errors # between log2 and exp2 to eventually compute the identity function # test-case #3 result = ConditionBlock( LogicalOr(LogicalOr(Test(vx, specifier=Test.IsNaN), Equal(n, 0)), LogicalAnd(n_is_even, vx < 0)), Return(FP_QNaN(self.precision)), Statement( ConditionBlock( Equal(n, -1, tag="n_is_mone"), #Return(Division(Constant(1, precision=self.precision), unmodified_vx, tag="div_res", precision=self.precision)), Return( ml_division_fct(Constant(1, precision=self.precision), unmodified_vx, tag="div_res", precision=self.precision)), ), ConditionBlock( # rootn( ±inf, n) is +∞ for even n< 0. Test(vx, specifier=Test.IsInfty), Statement( ConditionBlock( n < 0, #LogicalAnd(n_is_odd, n < 0), Return( Select(Test(vx, specifier=Test.IsPositiveInfty), Constant(FP_PlusZero(self.precision), precision=self.precision), Constant(FP_MinusZero(self.precision), precision=self.precision), precision=self.precision)), Return(vx), ), ), ), ConditionBlock( # rootn(±0, n) is ±∞ for odd n < 0. LogicalAnd(LogicalAnd(n_is_odd, n < 0), Equal(vx, 0), tag="n_is_odd_and_neg"), Return( Select(Test(vx, specifier=Test.IsPositiveZero), Constant(FP_PlusInfty(self.precision), precision=self.precision), Constant(FP_MinusInfty(self.precision), precision=self.precision), precision=self.precision)), ), ConditionBlock( # rootn( ±0, n) is +∞ for even n< 0. LogicalAnd(LogicalAnd(n_is_even, n < 0), Equal(vx, 0)), Return(FP_PlusInfty(self.precision))), ConditionBlock( # rootn(±0, n) is +0 for even n > 0. LogicalAnd(n_is_even, Equal(vx, 0)), Return(vx)), ConditionBlock( Equal(n, 1), Return(unmodified_vx), Return(result_sign * exp2_r * exp2_e_n_int * exp2_e_n_frac)))) return result
def standard_test_cases(self): general_list = [ # ERROR: rootn: inf ulp error at {inf, -2}: *0x0p+0 vs. inf (0x7f800000) at index: 1226 (FP_PlusInfty(self.precision), -2, FP_PlusZero(self.precision)), # ERROR: rootn: inf ulp error at {inf, -2147483648}: *0x0.0000000000000p+0 vs. inf (FP_PlusInfty(self.precision), -2147483648, FP_PlusZero(self.precision)), # (FP_PlusZero(self.precision), -1, FP_PlusInfty(self.precision)), (FP_MinusInfty(self.precision), 1, FP_MinusInfty(self.precision)), (FP_MinusInfty(self.precision), -1, FP_MinusZero(self.precision)), # ERROR coucou7: rootn: -inf ulp error at {inf 7f800000, 479638026}: *inf vs. 0x1.000018p+0 (0x3f80000c) at index: 2367 (FP_PlusInfty(self.precision), 479638026, FP_PlusInfty(self.precision)), (FP_MinusInfty(self.precision), 479638026), #(FP_MinusInfty(self.precision), -479638026), #(FP_PlusInfty(self.precision), -479638026), # rootn( ±0, n) is ±∞ for odd n< 0. (FP_PlusZero(self.precision), -1337, FP_PlusInfty(self.precision)), (FP_MinusZero(self.precision), -1337, FP_MinusInfty(self.precision)), # rootn( ±0, n) is +∞ for even n< 0. (FP_PlusZero(self.precision), -1330, FP_PlusInfty(self.precision)), # rootn( ±0, n) is +0 for even n> 0. (FP_PlusZero(self.precision), random.randrange(0, 2**31, 2), FP_PlusZero(self.precision)), (FP_MinusZero(self.precision), random.randrange(0, 2**31, 2), FP_PlusZero(self.precision)), # rootn( ±0, n) is ±0 for odd n> 0. (FP_PlusZero(self.precision), random.randrange(1, 2**31, 2), FP_PlusZero(self.precision)), (FP_MinusZero(self.precision), random.randrange(1, 2**31, 2), FP_MinusZero(self.precision)), # rootn( x, n) returns a NaN for x< 0 and n is even. (-random.random(), 2 * random.randrange(1, 2**30), FP_QNaN(self.precision)), # rootn( x, 0 ) returns a NaN (random.random(), 0, FP_QNaN(self.precision)), # vx=nan (sollya.parse("-nan"), -1811577079, sollya.parse("nan")), (sollya.parse("-nan"), 832501219, sollya.parse("nan")), (sollya.parse("-nan"), -857435762, sollya.parse("nan")), (sollya.parse("-nan"), -1503049611, sollya.parse("nan")), (sollya.parse("-nan"), 2105620996, sollya.parse("nan")), #ERROR: rootn: inf ulp error at {-nan, 832501219}: *-nan vs. -0x1.00000df2bed98p+1 #ERROR: rootn: inf ulp error at {-nan, -857435762}: *-nan vs. 0x1.0000000000000p+1 #ERROR: rootn: inf ulp error at {-nan, -1503049611}: *-nan vs. -0x1.0000000000000p+1 #ERROR: rootn: inf ulp error at {-nan, 2105620996}: *-nan vs. 0x1.00000583c4b7ap+1 (sollya.parse("-0x1.cd150ap-105"), 105297051), (sollya.parse("0x1.ec3bf8p+71"), -1650769017), # test-case #12 (0.1, 17), # test-case #11, fails in OpenCL CTS (sollya.parse("0x0.000000001d600p-1022"), 14), # test-case #10, fails test with dar(2**-23) (sollya.parse("-0x1.20aadp-114"), 17), # test-case #9 (sollya.parse("0x1.a44d8ep+121"), 7), # test-case #8 (sollya.parse("-0x1.3ef124p+103"), 3), # test-case #7 (sollya.parse("-0x1.01047ep-2"), 39), # test-case #6 (sollya.parse("-0x1.0105bp+67"), 23), # test-case #5 (sollya.parse("0x1.c1f72p+51"), 6), # special cases (sollya.parse("0x0p+0"), 1), (sollya.parse("0x0p+0"), 0), # test-case #3, catastrophic error for n=1 (sollya.parse("0x1.fc61a2p-121"), 1.0), # test-case #4 , k=14 < 0 not supported by bigfloat # (sollya.parse("0x1.ad067ap-66"), -14), ] # NOTE: expected value assumed 32-bit precision output fp_32_only = [ # (sollya.parse("0x1.80bb0ep+70"), 377778829, sollya.parse("0x1.000002p+0")), ] # NOTE: the following test-case are only valid if meta-function supports 64-bit integer # 2nd_input fp_64_only = [ (sollya.parse("0x1.fffffffffffffp+1023"), -1, sollya.parse("0x0.4000000000000p-1022")), (sollya.parse("-0x1.fffffffffffffp1023"), -1, sollya.parse("-0x0.4000000000000p-1022")), #(sollya.parse("-0x1.fffffffffffffp+1023"), 1), #(sollya.parse("0x1.fffffffffffffp+1023"), -1), # ERROR coucou8: rootn: inf ulp error at {-inf, 1854324695}: *-inf vs. -0x1.0000066bfdd60p+0 (FP_MinusInfty(self.precision), 1854324695, FP_MinusInfty(self.precision)), # ERROR: rootn: -60.962402 ulp error at {0x0.000000001d600p-1022, 14}: *0x1.67d4ff97d1fd9p-76 vs. 0x1.67d4ff97d1f9cp-76 (sollya.parse("0x0.000000001d600p-1022"), 14, sollya.parse("0x1.67d4ff97d1fd9p-76")), # ERROR: rootn: -430452000.000000 ulp error at {0x1.ffffffff38c00p-306, 384017876}: *0x1.ffffed870ff01p-1 vs. 0x1.ffffebec8d1d2p-1 (sollya.parse("0x1.ffffffff38c00p-306"), 384017876, sollya.parse("0x1.ffffed870ff01p-1")), # vs. 0x1.ffffebec8d1d2p-1 # ERROR: rootn: 92996584.000000 ulp error at {0x1.ffffffffdae80p-858, -888750231}: *0x1.00000b36b1173p+0 vs. 0x1.00000b8f6155ep+0 (sollya.parse("0x1.ffffffffdae80p-858"), -888750231, sollya.parse("0x1.00000b36b1173p+0")), # ERROR: rootn: 379474.906250 ulp error at {0x0.0000000000022p-1022, -1538297900}: *0x1.00000814a68ffp+0 vs. 0x1.0000081503352p+0 (sollya.parse("0x0.00000006abfffp-1022"), -1221802473, sollya.parse("0x1.00000a01818a4p+0")), (sollya.parse("0x1.ffffffffd0a00p-260"), 1108043946, sollya.parse("0x1.fffffa9042997p-1")), (sollya.parse("0x1.3fffffffff1c0p-927"), -1997086266, sollya.parse("0x1.0000056564c5ep+0")), (sollya.parse("0x1.ffffffff38c00p-306"), 384017876, sollya.parse("0x1.ffffed870ff01p-1")), (sollya.parse("0x0.15c000000002ap-1022"), 740015941, sollya.parse("0x1.ffffdfc47b57ep-1")), (sollya.parse("0x0.00000000227ffp-1022"), -1859058847, sollya.parse("0x1.0000069c7a01bp+0")), (sollya.parse("0x0.0568000000012p-1022"), -447352599, sollya.parse("0x1.00001ab640c38p+0")), (sollya.parse("0x0.000000000000dp-1022"), 132283432, sollya.parse("0x1.ffff43d1db82ap-1")), (sollya.parse("-0x1.c80000000026ap+1023"), 275148531, sollya.parse("-0x1.00002b45a7314p+0")), (sollya.parse("0x0.022200000000ep-1022"), -1969769414, sollya.parse("0x1.000006130e858p+0")), (sollya.parse("0x0.0000000000011p-1022"), 851990770, sollya.parse("0x1.ffffe2cafaff6p-1")), (sollya.parse("0x1.8fffffffff348p-1010"), 526938360, sollya.parse("0x1.ffffd372e2b81p-1")), (sollya.parse("0x0.0000000000317p-1022"), -1315106194, sollya.parse("0x1.0000096973ac9p+0")), (sollya.parse("0x1.1ffffffff2d20p-971"), 378658008, sollya.parse("0x1.ffffc45e803b2p-1")), # (sollya.parse("0x0.0568000000012p-1022"), -447352599, sollya.parse("0x1.00001ab640c38p+0")), # (sollya.parse("0x1.ffffffffd0a00p-260"), 1108043946, sollya.parse("0x1.fffffa9042997p-1")), (FP_MinusZero(self.precision), -21015979, FP_MinusInfty(self.precision)), (FP_MinusZero(self.precision), -85403731, FP_MinusInfty(self.precision)), (FP_MinusZero(self.precision), -180488973, FP_MinusInfty(self.precision)), (FP_MinusZero(self.precision), -1365227287, FP_MinusInfty(self.precision)), (FP_MinusZero(self.precision), -1802885579, FP_MinusInfty(self.precision)), (FP_MinusZero(self.precision), -1681209663, FP_MinusInfty(self.precision)), (FP_MinusZero(self.precision), -1152797721, FP_MinusInfty(self.precision)), (FP_MinusZero(self.precision), -1614890585, FP_MinusInfty(self.precision)), (FP_MinusZero(self.precision), -812655517, FP_MinusInfty(self.precision)), (FP_MinusZero(self.precision), -628647891, FP_MinusInfty(self.precision)), (sollya.parse("0x1.ffffffffdae80p-858"), -888750231, sollya.parse("0x1.00000b36b1173p+0")), (sollya.parse("0x0.0568000000012p-1022"), -447352599, sollya.parse("0x1.00001ab640c38p+0")), (sollya.parse("0x0.00000006abfffp-1022"), -1221802473, sollya.parse("0x1.00000a01818a4p+0")), (sollya.parse("0x0.0000000000022p-1022"), -1538297900, sollya.parse("0x1.00000814a68ffp+0")), #ERROR: rootn: inf ulp error at {-0x0.0000000000000p+0, -1889147085}: *-inf vs. inf #ERROR: rootn: inf ulp error at {-0x0.0000000000000p+0, -373548013}: *-inf vs. inf (FP_MinusZero(self.precision), -1889147085, FP_MinusInfty(self.precision)), (FP_MinusZero(self.precision), -373548013, FP_MinusInfty(self.precision)), #ERROR: rootn: inf ulp error at {-0x0.0000000000000p+0, -1889147085}: *-inf vs. inf #ERROR: rootn: inf ulp error at {-0x0.0000000000000p+0, -373548013}: *-inf vs. inf # [email protected]: PE 0: error[84]: ml_rootn(-0x1.b1a6765727e72p-902, -7.734955e+08/-773495525), result is -0x1.00000d8cb5b3cp+0 vs expected [nan;nan] (sollya.parse("-0x1.b1a6765727e72p-902"), -773495525), # ERROR: rootn: -40564819207303340847894502572032.000000 ulp error at {-0x0.fffffffffffffp-1022, 1}: *-0x0.fffffffffffffp-1022 vs. -0x1.ffffffffffffep-970 (sollya.parse("-0x0.fffffffffffffp-1022 "), 1, sollya.parse("-0x0.fffffffffffffp-1022 ")), # ERROR: rootn: 1125899906842624.000000 ulp error at {-0x1.fffffffffffffp+1023, -1}: *-0x0.4000000000000p-1022 vs. -0x0.0000000000000p+0 (sollya.parse("-0x1.fffffffffffffp+1023"), -1, sollya.parse("-0x0.4000000000000p-1022")), (sollya.parse("0x1.fffffffffffffp+1023"), -1, sollya.parse("0x0.4000000000000p-1022")), ] return (fp_64_only if self.precision.get_bit_size() >= 64 else []) \ + (fp_32_only if self.precision.get_bit_size() == 32 else []) \ + general_list
def generate_scheme(self): # declaring target and instantiating optimization engine vx = self.implementation.add_input_variable("x", self.precision) vx.set_attributes(precision=self.precision, tag="vx", debug=debug_multi) Log.set_dump_stdout(True) Log.report(Log.Info, "\033[33;1m Generating implementation scheme \033[0m") if self.debug_flag: Log.report(Log.Info, "\033[31;1m debug has been enabled \033[0;m") # local overloading of RaiseReturn operation def SqrtRaiseReturn(*args, **kwords): kwords["arg_value"] = vx kwords["function_name"] = self.function_name return RaiseReturn(*args, **kwords) C0 = Constant(0, precision=self.precision) C0_plus = Constant(FP_PlusZero(self.precision)) test_NaN = Test(vx, specifier=Test.IsNaN, likely=False, debug=debug_multi, tag="is_NaN", precision=ML_Bool) test_negative = Comparison(vx, C0, specifier=Comparison.Less, debug=debug_multi, tag="is_Negative", precision=ML_Bool, likely=False) test_zero = Comparison(vx, C0_plus, specifier=Comparison.Equal, likely=False, debug=debug_multi, tag="Is_Zero", precision=ML_Bool) test_inf = Test(vx, specifier=Test.IsInfty, likely=False, debug=debug_multi, tag="is_Inf", precision=ML_Bool) test_NaN_or_Neg = LogicalOr(test_NaN, test_negative, precision=ML_Bool, likely=False) test_NaN_or_Inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, debug=debug_multi, tag="is_nan_or_inf", precision=ML_Bool) test_negative_or_zero = Comparison(vx, C0, specifier=Comparison.LessOrEqual, debug=debug_multi, tag="is_Negative_or_zero", precision=ML_Bool, likely=False) test_std = LogicalNot(LogicalOr(test_NaN_or_Inf, test_negative_or_zero, precision=ML_Bool, likely=False), precision=ML_Bool, likely=True) return_PosZero = Statement(Return(FP_PlusInfty(self.precision))) return_NegZero = Statement(Return(FP_MinusInfty(self.precision))) return_NaN_or_neg = Statement(Return(FP_QNaN(self.precision))) return_inf = Statement(Return(C0)) NR_init = ReciprocalSquareRootSeed(vx, precision=self.precision, tag="sqrt_seed", debug=debug_multi) result = compute_isqrt(vx, NR_init, self.num_iter, self.precision) return_non_std = ConditionBlock( test_NaN_or_Neg, return_NaN_or_neg, ConditionBlock( test_inf, return_inf, ConditionBlock(test_zero, return_PosZero, return_NegZero))) scheme = Statement( ConditionBlock(test_std, Statement(Return(result)), Statement(return_non_std))) return scheme
def generate_scheme(self): vx = self.implementation.add_input_variable("x", self.get_input_precision()) sollya_precision = self.get_input_precision().get_sollya_object() log_f = sollya.log(sollya.x) # /sollya.log(self.basis) # local overloading of RaiseReturn operation def ExpRaiseReturn(*args, **kwords): kwords["arg_value"] = vx kwords["function_name"] = self.function_name return RaiseReturn(*args, **kwords) test_nan_or_inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, debug=True, tag="nan_or_inf") test_nan = Test(vx, specifier=Test.IsNaN, debug=True, tag="is_nan_test") test_positive = Comparison(vx, 0, specifier=Comparison.GreaterOrEqual, debug=True, tag="inf_sign") test_signaling_nan = Test(vx, specifier=Test.IsSignalingNaN, debug=True, tag="is_signaling_nan") return_snan = Statement( ExpRaiseReturn(ML_FPE_Invalid, return_value=FP_QNaN(self.precision))) log2_hi_value = round( log_f(2), self.precision.get_field_size() - (self.precision.get_exponent_size() + 1), RN) log2_lo_value = round( log_f(2) - log2_hi_value, self.precision.sollya_object, RN) log2_hi = Constant(log2_hi_value, precision=self.precision) log2_lo = Constant(log2_lo_value, precision=self.precision) int_precision = self.precision.get_integer_format() vx_exp = ExponentExtraction(vx, tag="vx_exp", debug=debug_multi) #--------------------- # Approximation scheme #--------------------- # log10(x) = log10(m.2^e) = log10(m.2^(e-t+t)) # = log10(m.2^-t) + (e+t) log10(2) # t = (m > sqrt(2)) ? 1 : 0 is used to avoid catastrophic cancellation # when e = -1 and m ~ 2 # # # log10(m.2^-t) = log10(m.r/r.2^-t) = log10(m.r) + log10(2^-t/r) # = log10(m.r) - log10(r.2^t) # where r = rcp(m) an approximation of 1/m such that r.m ~ 1 # retrieving processor inverse approximation table dummy_var = Variable("dummy", precision=self.precision) dummy_div_seed = ReciprocalSeed(dummy_var, precision=self.precision) inv_approx_table = self.processor.get_recursive_implementation( dummy_div_seed, language=None, table_getter=lambda self: self.approx_table_map) # table creation table_index_size = inv_approx_table.index_size table_index_range = range(1, 2**table_index_size) log_table = ML_NewTable(dimensions=[2**table_index_size, 2], storage_precision=self.precision) log_table_tho = ML_NewTable(dimensions=[2**table_index_size, 2], storage_precision=self.precision) log_table[0][0] = 0.0 log_table[0][1] = 0.0 log_table_tho[0][0] = 0.0 log_table_tho[0][1] = 0.0 hi_size = self.precision.get_field_size() - ( self.precision.get_exponent_size() + 1) for i in table_index_range: #inv_value = (1.0 + (self.processor.inv_approx_table[i] / S2**9) + S2**-52) * S2**-1 #inv_value = (1.0 + (inv_approx_table[i][0] / S2**9) ) * S2**-1 inv_value = inv_approx_table[i] value_high = round(log_f(inv_value), hi_size, sollya.RN) value_low = round( log_f(inv_value) - value_high, sollya_precision, sollya.RN) log_table[i][0] = value_high log_table[i][1] = value_low inv_value_tho = S2 * inv_approx_table[i] value_high_tho = round(log_f(inv_value_tho), hi_size, sollya.RN) value_low_tho = round( log_f(inv_value_tho) - value_high_tho, sollya_precision, sollya.RN) log_table_tho[i][0] = value_high_tho log_table_tho[i][1] = value_low_tho # determining log_table range high_index_function = lambda table, i: table[i][0] low_index_function = lambda table, i: table[i][1] table_high_interval = log_table.get_subset_interval( high_index_function, table_index_range) table_low_interval = log_table.get_subset_interval( low_index_function, table_index_range) def compute_log(_vx, exp_corr_factor=None): _vx_mant = MantissaExtraction(_vx, tag="_vx_mant", precision=self.precision, debug=debug_multi) _vx_exp = ExponentExtraction(_vx, tag="_vx_exp", debug=debug_multi) table_index = inv_approx_table.index_function(_vx_mant) table_index.set_attributes(tag="table_index", debug=debug_multi) tho_cond = _vx_mant > Constant(sollya.sqrt(2), precision=self.precision) tho = Select(tho_cond, Constant(1.0, precision=self.precision), Constant(0.0, precision=self.precision), precision=self.precision, tag="tho", debug=debug_multi) rcp = ReciprocalSeed(_vx_mant, precision=self.precision, tag="rcp") r = Multiplication(rcp, _vx_mant, precision=self.precision, tag="r") int_format = self.precision.get_integer_format() # argument reduction # TODO: detect if single operand inverse seed is supported by the targeted architecture pre_arg_red_index = TypeCast(BitLogicAnd( TypeCast(ReciprocalSeed(_vx_mant, precision=self.precision, tag="seed", debug=debug_multi, silent=True), precision=int_format), Constant(-2, precision=int_format), precision=int_format), precision=self.precision, tag="pre_arg_red_index", debug=debug_multi) arg_red_index = Select(Equal(table_index, 0), 1.0, pre_arg_red_index, tag="arg_red_index", debug=debug_multi) _red_vx = arg_red_index * _vx_mant - 1.0 inv_err = S2**-6 red_interval = Interval(1 - inv_err, 1 + inv_err) _red_vx.set_attributes(tag="_red_vx", debug=debug_multi, interval=red_interval) # return in case of standard (non-special) input _log_inv_lo = Select(tho_cond, TableLoad(log_table_tho, table_index, 1), TableLoad(log_table, table_index, 1), tag="log_inv_lo", debug=debug_multi) _log_inv_hi = Select(tho_cond, TableLoad(log_table_tho, table_index, 0), TableLoad(log_table, table_index, 0), tag="log_inv_hi", debug=debug_multi) Log.report(Log.Info, "building mathematical polynomial") approx_interval = Interval(-inv_err, inv_err) poly_degree = sup( guessdegree( log(1 + sollya.x) / sollya.x, approx_interval, S2** -(self.precision.get_field_size() + 1))) + 1 global_poly_object = Polynomial.build_from_approximation( log(1 + x) / x, poly_degree, [self.precision] * (poly_degree + 1), approx_interval, sollya.absolute) poly_object = global_poly_object.sub_poly(start_index=1) Log.report(Log.Info, "generating polynomial evaluation scheme") _poly = PolynomialSchemeEvaluator.generate_horner_scheme( poly_object, _red_vx, unified_precision=self.precision) _poly.set_attributes(tag="poly", debug=debug_multi) Log.report(Log.Info, poly_object.get_sollya_object()) corr_exp = Conversion(_vx_exp if exp_corr_factor == None else _vx_exp + exp_corr_factor, precision=self.precision) + tho corr_exp.set_attributes(tag="corr_exp", debug=debug_multi) # _poly approximates log10(1+r)/r # _poly * red_vx approximates log10(x) m0h, m0l = Mul211(_red_vx, _poly) m0h, m0l = Add212(_red_vx, m0h, m0l) m0h.set_attributes(tag="m0h", debug=debug_multi) m0l.set_attributes(tag="m0l") l0_h = corr_exp * log2_hi l0_l = corr_exp * log2_lo l0_h.set_attributes(tag="l0_h") l0_l.set_attributes(tag="l0_l") rh, rl = Add222(l0_h, l0_l, m0h, m0l) rh.set_attributes(tag="rh0", debug=debug_multi) rl.set_attributes(tag="rl0", debug=debug_multi) rh, rl = Add222(-_log_inv_hi, -_log_inv_lo, rh, rl) rh.set_attributes(tag="rh", debug=debug_multi) rl.set_attributes(tag="rl", debug=debug_multi) if sollya.log(self.basis) != 1.0: lbh = self.precision.round_sollya_object( 1 / sollya.log(self.basis)) lbl = self.precision.round_sollya_object( 1 / sollya.log(self.basis) - lbh) rh, rl = Mul222(rh, rl, lbh, lbl) return rh else: return rh result = compute_log(vx) result.set_attributes(tag="result", debug=debug_multi) if False: # building eval error map eval_error_map = { red_vx: Variable("red_vx", precision=self.precision, interval=red_vx.get_interval()), log_inv_hi: Variable("log_inv_hi", precision=self.precision, interval=table_high_interval), log_inv_lo: Variable("log_inv_lo", precision=self.precision, interval=table_low_interval), corr_exp: Variable("corr_exp_g", precision=self.precision, interval=self.precision.get_exponent_interval()), } # computing gappa error if is_gappa_installed(): poly_eval_error = self.get_eval_error(result, eval_error_map) Log.report(Log.Info, "poly_eval_error: ", poly_eval_error) neg_input = Comparison(vx, 0, likely=False, specifier=Comparison.Less, debug=debug_multi, tag="neg_input") vx_nan_or_inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, debug=debug_multi, tag="nan_or_inf") vx_snan = Test(vx, specifier=Test.IsSignalingNaN, likely=False, debug=debug_multi, tag="snan") vx_inf = Test(vx, specifier=Test.IsInfty, likely=False, debug=debug_multi, tag="inf") vx_subnormal = Test(vx, specifier=Test.IsSubnormal, likely=False, debug=debug_multi, tag="vx_subnormal") vx_zero = Test(vx, specifier=Test.IsZero, likely=False, debug=debug_multi, tag="vx_zero") exp_mone = Equal(vx_exp, -1, tag="exp_minus_one", debug=debug_multi, likely=False) # exp=-1 case Log.report(Log.Info, "managing exp=-1 case") #red_vx_2 = arg_red_index * vx_mant * 0.5 #approx_interval2 = Interval(0.5 - inv_err, 0.5 + inv_err) #poly_degree2 = sup(guessdegree(log(x), approx_interval2, S2**-(self.precision.get_field_size()+1))) + 1 #poly_object2 = Polynomial.build_from_approximation(log(sollya.x), poly_degree, [self.precision]*(poly_degree+1), approx_interval2, sollya.absolute) #print "poly_object2: ", poly_object2.get_sollya_object() #poly2 = PolynomialSchemeEvaluator.generate_horner_scheme(poly_object2, red_vx_2, unified_precision = self.precision) #poly2.set_attributes(tag = "poly2", debug = debug_multi) #result2 = (poly2 - log_inv_hi - log_inv_lo) m100 = -100 S2100 = Constant(S2**100, precision=self.precision) result_subnormal = compute_log(vx * S2100, exp_corr_factor=m100) # main scheme Log.report(Log.Info, "MDL scheme") pre_scheme = ConditionBlock( neg_input, Statement(ClearException(), Raise(ML_FPE_Invalid), Return(FP_QNaN(self.precision))), ConditionBlock( vx_nan_or_inf, ConditionBlock( vx_inf, Statement( ClearException(), Return(FP_PlusInfty(self.precision)), ), Statement(ClearException(), ConditionBlock(vx_snan, Raise(ML_FPE_Invalid)), Return(FP_QNaN(self.precision)))), ConditionBlock( vx_subnormal, ConditionBlock( vx_zero, Statement( ClearException(), Raise(ML_FPE_DivideByZero), Return(FP_MinusInfty(self.precision)), ), Return(result_subnormal)), Return(result)))) scheme = pre_scheme return scheme
def generate_scalar_scheme(self, vx): # local overloading of RaiseReturn operation def ExpRaiseReturn(*args, **kwords): kwords["arg_value"] = vx kwords["function_name"] = self.function_name return RaiseReturn(*args, **kwords) test_nan_or_inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, debug=True, tag="nan_or_inf") test_nan = Test(vx, specifier=Test.IsNaN, debug=True, tag="is_nan_test") test_positive = Comparison(vx, 0, specifier=Comparison.GreaterOrEqual, debug=True, tag="inf_sign") test_signaling_nan = Test(vx, specifier=Test.IsSignalingNaN, debug=True, tag="is_signaling_nan") return_snan = Statement( ExpRaiseReturn(ML_FPE_Invalid, return_value=FP_QNaN(self.precision))) int_precision = self.precision.get_integer_format() vx_exp = ExponentExtraction(vx, tag="vx_exp", precision=int_precision, debug=debug_multi) #--------------------- # Approximation scheme #--------------------- # log(x) = log(m.2^e) = log(m.2^(e-tho+tho)) # = log(m.2^-tho) + (e+tho) log(2) # tho = (m > sqrt(2)) ? 1 : 0 is used to avoid catastrophic cancellation # when e = -1 and m ~ 2 # # # log(m.2^-tho) = log(m.r/r.2^-tho) = log(m.r) + log(2^-tho/r) # = log(m.r) - log(r.2^tho) # where r = rcp(m) an approximation of 1/m such that r.m ~ 1 # retrieving processor inverse approximation table dummy_var = Variable("dummy", precision=self.precision) dummy_div_seed = ReciprocalSeed(dummy_var, precision=self.precision) # table of the reciprocal approximation of the targeted processor inv_approx_table = self.processor.get_recursive_implementation( dummy_div_seed, language=None, table_getter=lambda self: self.approx_table_map) log_f = sollya.log(sollya.x) # /sollya.log(self.basis) log_table, log_table_tho, table_index_range = self.generate_log_table( log_f, inv_approx_table) # determining log_table range high_index_function = lambda table, i: table[i][0] low_index_function = lambda table, i: table[i][1] table_high_interval = log_table.get_subset_interval( high_index_function, table_index_range) table_low_interval = log_table.get_subset_interval( low_index_function, table_index_range) result = self.generate_reduced_log(vx, log_f, inv_approx_table, log_table, log_table_tho) result.set_attributes(tag="result", debug=debug_multi) if False: # building eval error map eval_error_map = { red_vx: Variable("red_vx", precision=self.precision, interval=red_vx.get_interval()), log_inv_hi: Variable("log_inv_hi", precision=self.precision, interval=table_high_interval), log_inv_lo: Variable("log_inv_lo", precision=self.precision, interval=table_low_interval), corr_exp: Variable("corr_exp_g", precision=self.precision, interval=self.precision.get_exponent_interval()), } # computing gappa error if is_gappa_installed(): poly_eval_error = self.get_eval_error(result, eval_error_map) Log.report(Log.Info, "poly_eval_error: ", poly_eval_error) neg_input = Comparison(vx, 0, likely=False, specifier=Comparison.Less, debug=debug_multi, tag="neg_input") vx_nan_or_inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, debug=debug_multi, tag="nan_or_inf") vx_snan = Test(vx, specifier=Test.IsSignalingNaN, likely=False, debug=debug_multi, tag="snan") vx_inf = Test(vx, specifier=Test.IsInfty, likely=False, debug=debug_multi, tag="inf") vx_subnormal = Test(vx, specifier=Test.IsSubnormal, likely=False, debug=debug_multi, tag="vx_subnormal") vx_zero = Test(vx, specifier=Test.IsZero, likely=False, debug=debug_multi, tag="vx_zero") exp_mone = Equal(vx_exp, -1, tag="exp_minus_one", debug=debug_multi, likely=False) # exp=-1 case Log.report(Log.Info, "managing exp=-1 case") #red_vx_2 = arg_red_index * vx_mant * 0.5 #approx_interval2 = Interval(0.5 - inv_err, 0.5 + inv_err) #poly_degree2 = sup(guessdegree(log(x), approx_interval2, S2**-(self.precision.get_field_size()+1))) + 1 #poly_object2 = Polynomial.build_from_approximation(log(sollya.x), poly_degree, [self.precision]*(poly_degree+1), approx_interval2, sollya.absolute) #print "poly_object2: ", poly_object2.get_sollya_object() #poly2 = PolynomialSchemeEvaluator.generate_horner_scheme(poly_object2, red_vx_2, unified_precision = self.precision) #poly2.set_attributes(tag = "poly2", debug = debug_multi) #result2 = (poly2 - log_inv_hi - log_inv_lo) m100 = Constant(-100, precision=int_precision) S2100 = Constant(S2**100, precision=self.precision) result_subnormal = self.generate_reduced_log(vx * S2100, log_f, inv_approx_table, log_table, log_table_tho, exp_corr_factor=m100) # main scheme Log.report(Log.Info, "MDL scheme") pre_scheme = ConditionBlock( neg_input, Statement( ClearException(), Raise(ML_FPE_Invalid), Return(FP_QNaN(self.precision), precision=self.precision)), ConditionBlock( vx_nan_or_inf, ConditionBlock( vx_inf, Statement( ClearException(), Return(FP_PlusInfty(self.precision), precision=self.precision), ), Statement( ClearException(), ConditionBlock(vx_snan, Raise(ML_FPE_Invalid)), Return(FP_QNaN(self.precision), precision=self.precision))), ConditionBlock( vx_subnormal, ConditionBlock( vx_zero, Statement( ClearException(), Raise(ML_FPE_DivideByZero), Return(FP_MinusInfty(self.precision), precision=self.precision), ), Return(result_subnormal)), Return(result)))) scheme = pre_scheme return scheme
def generate_scheme(self): #func_implementation = CodeFunction(self.function_name, output_format = self.precision) vx = self.implementation.add_input_variable("x", self.get_input_precision()) sollya_precision = self.get_input_precision().get_sollya_object() # local overloading of RaiseReturn operation def ExpRaiseReturn(*args, **kwords): kwords["arg_value"] = vx kwords["function_name"] = self.function_name return RaiseReturn(*args, **kwords) test_nan_or_inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, debug=True, tag="nan_or_inf") test_nan = Test(vx, specifier=Test.IsNaN, debug=True, tag="is_nan_test") test_positive = Comparison(vx, 0, specifier=Comparison.GreaterOrEqual, debug=True, tag="inf_sign") test_signaling_nan = Test(vx, specifier=Test.IsSignalingNaN, debug=True, tag="is_signaling_nan") return_snan = Statement( ExpRaiseReturn(ML_FPE_Invalid, return_value=FP_QNaN(self.precision))) log2_hi_value = round( log10(2), self.precision.get_field_size() - (self.precision.get_exponent_size() + 1), RN) log2_lo_value = round( log10(2) - log2_hi_value, self.precision.sollya_object, RN) log2_hi = Constant(log2_hi_value, precision=self.precision) log2_lo = Constant(log2_lo_value, precision=self.precision) vx_exp = ExponentExtraction(vx, tag="vx_exp", debug=debugd) int_precision = self.precision.get_integer_format() # retrieving processor inverse approximation table dummy_var = Variable("dummy", precision=self.precision) dummy_div_seed = DivisionSeed(dummy_var, precision=self.precision) inv_approx_table = self.processor.get_recursive_implementation( dummy_div_seed, language=None, table_getter=lambda self: self.approx_table_map) # table creation table_index_size = 7 table_index_range = range(1, 2**table_index_size) log_table = ML_NewTable(dimensions=[2**table_index_size, 2], storage_precision=self.precision) log_table[0][0] = 0.0 log_table[0][1] = 0.0 for i in table_index_range: #inv_value = (1.0 + (self.processor.inv_approx_table[i] / S2**9) + S2**-52) * S2**-1 #inv_value = (1.0 + (inv_approx_table[i][0] / S2**9) ) * S2**-1 inv_value = inv_approx_table[i][0] value_high = round( log10(inv_value), self.precision.get_field_size() - (self.precision.get_exponent_size() + 1), sollya.RN) value_low = round( log10(inv_value) - value_high, sollya_precision, sollya.RN) log_table[i][0] = value_high log_table[i][1] = value_low # determining log_table range high_index_function = lambda table, i: table[i][0] low_index_function = lambda table, i: table[i][1] table_high_interval = log_table.get_subset_interval( high_index_function, table_index_range) table_low_interval = log_table.get_subset_interval( low_index_function, table_index_range) def compute_log(_vx, exp_corr_factor=None): _vx_mant = MantissaExtraction(_vx, tag="_vx_mant", debug=debug_lftolx) _vx_exp = ExponentExtraction(_vx, tag="_vx_exp", debug=debugd) table_index = BitLogicAnd(BitLogicRightShift( TypeCast(_vx_mant, precision=int_precision, debug=debuglx), self.precision.get_field_size() - 7, debug=debuglx), 0x7f, tag="table_index", debug=debuglld) # argument reduction # TODO: detect if single operand inverse seed is supported by the targeted architecture pre_arg_red_index = TypeCast(BitLogicAnd( TypeCast(DivisionSeed(_vx_mant, precision=self.precision, tag="seed", debug=debug_lftolx, silent=True), precision=ML_UInt64), Constant(-2, precision=ML_UInt64), precision=ML_UInt64), precision=self.precision, tag="pre_arg_red_index", debug=debug_lftolx) arg_red_index = Select(Equal(table_index, 0), 1.0, pre_arg_red_index, tag="arg_red_index", debug=debug_lftolx) #if not processor.is_supported_operation(arg_red_index): # if self.precision != ML_Binary32: # arg_red_index = DivisionSeed(Conversion(_vx_mant, precision = ML_Binary32), precision = ML_Binary32, _red_vx = arg_red_index * _vx_mant - 1.0 inv_err = S2**-7 red_interval = Interval(1 - inv_err, 1 + inv_err) _red_vx.set_attributes(tag="_red_vx", debug=debug_lftolx, interval=red_interval) # return in case of standard (non-special) input _log_inv_lo = TableLoad(log_table, table_index, 1, tag="log_inv_lo", debug=debug_lftolx) _log_inv_hi = TableLoad(log_table, table_index, 0, tag="log_inv_hi", debug=debug_lftolx) print("building mathematical polynomial") approx_interval = Interval(-inv_err, inv_err) poly_degree = sup( guessdegree( log10(1 + sollya.x) / sollya.x, approx_interval, S2** -(self.precision.get_field_size() + 1))) + 1 global_poly_object = Polynomial.build_from_approximation( log10(1 + x) / x, poly_degree, [self.precision] * (poly_degree + 1), approx_interval, sollya.absolute) poly_object = global_poly_object #.sub_poly(start_index = 1) print("generating polynomial evaluation scheme") _poly = PolynomialSchemeEvaluator.generate_horner_scheme( poly_object, _red_vx, unified_precision=self.precision) _poly.set_attributes(tag="poly", debug=debug_lftolx) print(global_poly_object.get_sollya_object()) corr_exp = Conversion( _vx_exp if exp_corr_factor == None else _vx_exp + exp_corr_factor, precision=self.precision) split_red_vx = Split(_red_vx, precision=ML_DoubleDouble, tag="split_red_vx", debug=debug_ddtolx) red_vx_hi = split_red_vx.hi red_vx_lo = split_red_vx.lo # result = _red_vx * poly - log_inv_hi - log_inv_lo + _vx_exp * log2_hi + _vx_exp * log2_lo pre_result = -_log_inv_hi + ((_red_vx * _poly + (corr_exp * log2_lo - _log_inv_lo))) pre_result.set_attributes(tag="pre_result", debug=debug_lftolx) exact_log2_hi_exp = corr_exp * log2_hi exact_log2_hi_exp.set_attributes(tag="exact_log2_hi_hex", debug=debug_lftolx) cancel_part = (corr_exp * log2_hi - _log_inv_hi) cancel_part.set_attributes(tag="cancel_part", debug=debug_lftolx) sub_part = red_vx_hi + cancel_part sub_part.set_attributes(tag="sub_part", debug=debug_lftolx) #result_one_low_part = (red_vx_hi * _poly + (red_vx_lo + (red_vx_lo * _poly + (corr_exp * log2_lo - _log_inv_lo)))) result_one_low_part = ((red_vx_lo + (red_vx_lo * _poly + (corr_exp * log2_lo - _log_inv_lo)))) result_one_low_part.set_attributes(tag="result_one_low_part", debug=debug_lftolx) _result_one = ( (sub_part) + red_vx_hi * _poly) + result_one_low_part _result = exact_log2_hi_exp + pre_result return _result, _poly, _log_inv_lo, _log_inv_hi, _red_vx, _result_one, corr_exp result, poly, log_inv_lo, log_inv_hi, red_vx, new_result_one, corr_exp = compute_log( vx) result.set_attributes(tag="result", debug=debug_lftolx) new_result_one.set_attributes(tag="new_result_one", debug=debug_lftolx) # building eval error map eval_error_map = { red_vx: Variable("red_vx", precision=self.precision, interval=red_vx.get_interval()), log_inv_hi: Variable("log_inv_hi", precision=self.precision, interval=table_high_interval), log_inv_lo: Variable("log_inv_lo", precision=self.precision, interval=table_low_interval), corr_exp: Variable("corr_exp_g", precision=self.precision, interval=self.precision.get_exponent_interval()), } # computing gappa error if is_gappa_installed(): poly_eval_error = self.get_eval_error(result, eval_error_map) print("poly_eval_error: ", poly_eval_error) neg_input = Comparison(vx, 0, likely=False, specifier=Comparison.Less, debug=debugd, tag="neg_input") vx_nan_or_inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, debug=debugd, tag="nan_or_inf") vx_snan = Test(vx, specifier=Test.IsSignalingNaN, likely=False, debug=debugd, tag="snan") vx_inf = Test(vx, specifier=Test.IsInfty, likely=False, debug=debugd, tag="inf") vx_subnormal = Test(vx, specifier=Test.IsSubnormal, likely=False, debug=debugd, tag="vx_subnormal") vx_zero = Test(vx, specifier=Test.IsZero, likely=False, debug=debugd, tag="vx_zero") exp_mone = Equal(vx_exp, -1, tag="exp_minus_one", debug=debugd, likely=False) vx_one = Equal(vx, 1.0, tag="vx_one", likely=False, debug=debugd) # exp=-1 case print("managing exp=-1 case") #red_vx_2 = arg_red_index * vx_mant * 0.5 #approx_interval2 = Interval(0.5 - inv_err, 0.5 + inv_err) #poly_degree2 = sup(guessdegree(log(x), approx_interval2, S2**-(self.precision.get_field_size()+1))) + 1 #poly_object2 = Polynomial.build_from_approximation(log(sollya.x), poly_degree, [self.precision]*(poly_degree+1), approx_interval2, sollya.absolute) #print "poly_object2: ", poly_object2.get_sollya_object() #poly2 = PolynomialSchemeEvaluator.generate_horner_scheme(poly_object2, red_vx_2, unified_precision = self.precision) #poly2.set_attributes(tag = "poly2", debug = debug_lftolx) #result2 = (poly2 - log_inv_hi - log_inv_lo) log_subtract = -log_inv_hi - log2_hi log_subtract.set_attributes(tag="log_subtract", debug=debug_lftolx) result2 = (log_subtract) + ((poly * red_vx) - (log_inv_lo + log2_lo)) result2.set_attributes(tag="result2", debug=debug_lftolx) m100 = -100 S2100 = Constant(S2**100, precision=self.precision) result_subnormal, _, _, _, _, _, _ = compute_log(vx * S2100, exp_corr_factor=m100) print("managing close to 1.0 cases") one_err = S2**-7 approx_interval_one = Interval(-one_err, one_err) red_vx_one = vx - 1.0 poly_degree_one = sup( guessdegree( log10(1 + sollya.x) / sollya.x, approx_interval_one, S2** -(self.precision.get_field_size() + 1))) + 1 poly_object_one = Polynomial.build_from_approximation( log10(1 + sollya.x) / sollya.x, poly_degree_one, [self.precision] * (poly_degree_one + 1), approx_interval_one, sollya.absolute).sub_poly(start_index=1) poly_one = PolynomialSchemeEvaluator.generate_horner_scheme( poly_object_one, red_vx_one, unified_precision=self.precision) poly_one.set_attributes(tag="poly_one", debug=debug_lftolx) result_one = red_vx_one + red_vx_one * poly_one cond_one = (vx < (1 + one_err)) & (vx > (1 - one_err)) cond_one.set_attributes(tag="cond_one", debug=debugd, likely=False) # main scheme print("MDL scheme") pre_scheme = ConditionBlock( neg_input, Statement(ClearException(), Raise(ML_FPE_Invalid), Return(FP_QNaN(self.precision))), ConditionBlock( vx_nan_or_inf, ConditionBlock( vx_inf, Statement( ClearException(), Return(FP_PlusInfty(self.precision)), ), Statement(ClearException(), ConditionBlock(vx_snan, Raise(ML_FPE_Invalid)), Return(FP_QNaN(self.precision)))), ConditionBlock( vx_subnormal, ConditionBlock( vx_zero, Statement( ClearException(), Raise(ML_FPE_DivideByZero), Return(FP_MinusInfty(self.precision)), ), Return(result_subnormal)), ConditionBlock( vx_one, Statement( ClearException(), Return(FP_PlusZero(self.precision)), ), ConditionBlock(exp_mone, Return(result2), Return(result)) #ConditionBlock(cond_one, #Return(new_result_one), #ConditionBlock(exp_mone, #Return(result2), #Return(result) #) #) )))) scheme = pre_scheme return scheme