T float_advance(T val, int distance, const Policy& pol) { // // Error handling: // static const char* function = "float_advance<%1%>(%1%, int)"; if(!(boost::math::isfinite)(val)) return policies::raise_domain_error<T>( function, "Argument val must be finite, but got %1%", val, pol); if(val < 0) return -float_advance(-val, -distance, pol); if(distance == 0) return val; if(distance == 1) return float_next(val, pol); if(distance == -1) return float_prior(val, pol); BOOST_MATH_STD_USING int expon; frexp(val, &expon); T limit = ldexp((distance < 0 ? T(0.5f) : T(1)), expon); if(val <= tools::min_value<T>()) { limit = sign(T(distance)) * tools::min_value<T>(); } T limit_distance = float_distance(val, limit); while(fabs(limit_distance) < abs(distance)) { distance -= itrunc(limit_distance); val = limit; if(distance < 0) { limit /= 2; expon--; } else { limit *= 2; expon++; } limit_distance = float_distance(val, limit); } if((0.5f == frexp(val, &expon)) && (distance < 0)) --expon; T diff = 0; if(val != 0) diff = distance * ldexp(T(1), expon - tools::digits<T>()); if(diff == 0) diff = distance * detail::get_smallest_value<T>(); return val += diff; }
T float_prior(const T& val, const Policy& pol) { BOOST_MATH_STD_USING int expon; static const char* function = "float_prior<%1%>(%1%)"; int fpclass = (pdalboost::math::fpclassify)(val); if((fpclass == FP_NAN) || (fpclass == FP_INFINITE)) { if(val > 0) return tools::max_value<T>(); return policies::raise_domain_error<T>( function, "Argument must be finite, but got %1%", val, pol); } if(val <= -tools::max_value<T>()) return -policies::raise_overflow_error<T>(function, 0, pol); if(val == 0) return -detail::get_smallest_value<T>(); if((fpclass != FP_SUBNORMAL) && (fpclass != FP_ZERO) && (fabs(val) < detail::get_min_shift_value<T>()) && (val != tools::min_value<T>())) { // // Special case: if the value of the least significant bit is a denorm, and the result // would not be a denorm, then shift the input, increment, and shift back. // This avoids issues with the Intel SSE2 registers when the FTZ or DAZ flags are set. // return ldexp(float_prior(T(ldexp(val, 2 * tools::digits<T>())), pol), -2 * tools::digits<T>()); } T remain = frexp(val, &expon); if(remain == 0.5) --expon; // when val is a power of two we must reduce the exponent T diff = ldexp(T(1), expon - tools::digits<T>()); if(diff == 0) diff = detail::get_smallest_value<T>(); return val - diff; }
inline T float_prior(const T& val) { return float_prior(val, policies::policy<>()); }
T float_advance(T val, int distance, const Policy& pol) { BOOST_MATH_STD_USING // // Error handling: // static const char* function = "float_advance<%1%>(%1%, int)"; int fpclass = (pdalboost::math::fpclassify)(val); if((fpclass == FP_NAN) || (fpclass == FP_INFINITE)) return policies::raise_domain_error<T>( function, "Argument val must be finite, but got %1%", val, pol); if(val < 0) return -float_advance(-val, -distance, pol); if(distance == 0) return val; if(distance == 1) return float_next(val, pol); if(distance == -1) return float_prior(val, pol); if(fabs(val) < detail::get_min_shift_value<T>()) { // // Special case: if the value of the least significant bit is a denorm, // implement in terms of float_next/float_prior. // This avoids issues with the Intel SSE2 registers when the FTZ or DAZ flags are set. // if(distance > 0) { do{ val = float_next(val, pol); } while(--distance); } else { do{ val = float_prior(val, pol); } while(++distance); } return val; } int expon; frexp(val, &expon); T limit = ldexp((distance < 0 ? T(0.5f) : T(1)), expon); if(val <= tools::min_value<T>()) { limit = sign(T(distance)) * tools::min_value<T>(); } T limit_distance = float_distance(val, limit); while(fabs(limit_distance) < abs(distance)) { distance -= itrunc(limit_distance); val = limit; if(distance < 0) { limit /= 2; expon--; } else { limit *= 2; expon++; } limit_distance = float_distance(val, limit); if(distance && (limit_distance == 0)) { policies::raise_evaluation_error<T>(function, "Internal logic failed while trying to increment floating point value %1%: most likely your FPU is in non-IEEE conforming mode.", val, pol); } } if((0.5f == frexp(val, &expon)) && (distance < 0)) --expon; T diff = 0; if(val != 0) diff = distance * ldexp(T(1), expon - tools::digits<T>()); if(diff == 0) diff = distance * detail::get_smallest_value<T>(); return val += diff; }