From 60e5b84d78cc24f58bbb1a911fce7d764dc9af8a Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 24 May 2022 21:46:10 -0600 Subject: [PATCH] temporal: alternative solution to overflow in timeline operator*() This uses boost::multiprecision::int512_t when multiplying and dividing by the numerator and denominator of a ratio_t. 128 bits would be sufficient but for some reason, the boost docs show the 512 bit variant being very slightly faster. This is a better solution than using a double, which although it will prevent overflow has fairly limited resolution. --- libs/evoral/ControlList.cc | 3 +-- libs/temporal/timeline.cc | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/libs/evoral/ControlList.cc b/libs/evoral/ControlList.cc index 90028824bc..c8246ecc51 100644 --- a/libs/evoral/ControlList.cc +++ b/libs/evoral/ControlList.cc @@ -359,9 +359,8 @@ ControlList::list_merge (ControlList const& other, boost::functionwhen = timepos_t::from_superclock ((*i)->when.val() * double_factor + 0.5); + (*i)->when = (*i)->when.operator* (factor); } mark_dirty (); diff --git a/libs/temporal/timeline.cc b/libs/temporal/timeline.cc index 57c66e2ce8..15dcdecc1d 100644 --- a/libs/temporal/timeline.cc +++ b/libs/temporal/timeline.cc @@ -21,6 +21,8 @@ #include #include +#include + #include "pbd/enumwriter.h" #include "pbd/error.h" #include "pbd/compose.h" @@ -200,8 +202,23 @@ timecnt_t::compute_beats() const timecnt_t timecnt_t::operator*(ratio_t const & r) const { - const int62_t v (_distance.flagged(), int_div_round (_distance.val() * r.numerator(), r.denominator())); - return timecnt_t (v, _position); + boost::multiprecision::int512_t bignum = _distance.val(); + + bignum *= r.numerator (); + bignum /= r.denominator (); + + try { + + int64_t midnum = bignum.convert_to (); + assert (midnum < int62_t::max); + const int62_t v (_distance.flagged(), midnum); + return timecnt_t (v, _position); + + } catch (...) { + fatal << X_("arithmetic overflow in timeline math\n") << endmsg; + /* NOTREACHED */ + return timecnt_t (); + } } ratio_t @@ -589,7 +606,22 @@ timepos_t timepos_t::operator*(ratio_t const & n) const { /* this cannot make the value negative, since ratio_t is always positive */ - return timepos_t (is_beats(), int_div_round (val() * n.numerator(), n.denominator())); + boost::multiprecision::int512_t bignum = val(); + + bignum *= n.numerator (); + bignum /= n.denominator (); + + try { + + int64_t midnum = bignum.convert_to (); + assert (midnum < int62_t::max); + return timepos_t (is_beats(), midnum); + + } catch (...) { + fatal << X_("arithmetic overflow in timepos_t::operator* (ratio_t)\n") << endmsg; + /* NOTREACHED */ + return timepos_t(); + } } timepos_t &