77
88#include " tivarslib_utils.h"
99#include < cstdlib>
10+ #include < iomanip>
1011#include < sstream>
1112#include < cmath>
1213#include < cstring>
@@ -19,6 +20,8 @@ namespace tivars
1920
2021namespace
2122{
23+ constexpr long long dec2fracMaxDenominator = 100000 ;
24+
2225 void append_le16 (data_t & out, uint16_t value)
2326 {
2427 out.push_back (static_cast <uint8_t >(value & 0xFF ));
@@ -67,6 +70,15 @@ namespace
6770
6871 return out;
6972 }
73+
74+ std::string format_decimal_value (long double value)
75+ {
76+ std::ostringstream stream;
77+ stream << std::setprecision (std::numeric_limits<long double >::digits10 + 1 )
78+ << std::defaultfloat
79+ << value;
80+ return stream.str ();
81+ }
7082}
7183
7284unsigned char hexdec (const std::string& str)
@@ -291,14 +303,18 @@ std::string multiple(long long num, const std::string &var) {
291303// Adapted from http://stackoverflow.com/a/32903747/378298
292304std::string dec2frac (double num, const std::string& var, double err)
293305{
306+ const auto decimal_fallback = [&](long double value) {
307+ return trimZeros (format_decimal_value (value)) + var;
308+ };
309+
294310 if (err <= 0.0 || err >= 1.0 )
295311 {
296312 err = 0.001 ;
297313 }
298314
299315 if (!std::isfinite (num))
300316 {
301- return trimZeros (std::to_string (num)) + var;
317+ return trimZeros (format_decimal_value (num)) + var;
302318 }
303319
304320 const int sign = ( num > 0 ) ? 1 : ( ( num < 0 ) ? -1 : 0 );
@@ -318,7 +334,7 @@ std::string dec2frac(double num, const std::string& var, double err)
318334 if (num >= static_cast <double >(std::numeric_limits<long long >::max ())
319335 || num <= static_cast <double >(std::numeric_limits<long long >::lowest ()))
320336 {
321- return trimZeros ( std::to_string (sign * num)) + var ;
337+ return decimal_fallback ( static_cast < long double > (sign) * static_cast < long double >( num));
322338 }
323339
324340 const long long n = static_cast <long long >(std::floor (num));
@@ -342,17 +358,36 @@ std::string dec2frac(double num, const std::string& var, double err)
342358 long long upper_n = 1 ;
343359 long long upper_d = 1 ;
344360
361+ long long max_denominator = dec2fracMaxDenominator;
362+ const long double err_denominator_bound = std::ceil (1 .0L / static_cast <long double >(err));
363+ if (std::isfinite (err_denominator_bound))
364+ {
365+ max_denominator = std::min<long long >(max_denominator, std::max<long long >(1 , static_cast <long long >(err_denominator_bound)));
366+ }
367+
368+ size_t iterations = 0 ;
369+ const size_t max_iterations = static_cast <size_t >(max_denominator) * 2 ;
370+
345371 while (true )
346372 {
373+ if (++iterations > max_iterations)
374+ {
375+ return decimal_fallback (static_cast <long double >(sign) * (static_cast <long double >(n) + static_cast <long double >(num)));
376+ }
377+
347378 // The middle fraction is (lower_n + upper_n) / (lower_d + upper_d)
348379 if (lower_n > std::numeric_limits<long long >::max () - upper_n
349380 || lower_d > std::numeric_limits<long long >::max () - upper_d)
350381 {
351- return trimZeros ( std::to_string (sign * (n + num))) + var ;
382+ return decimal_fallback ( static_cast < long double > (sign) * (static_cast < long double >(n) + static_cast < long double >( num)));
352383 }
353384
354385 const long long middle_n = lower_n + upper_n;
355386 const long long middle_d = lower_d + upper_d;
387+ if (middle_d > max_denominator)
388+ {
389+ return decimal_fallback (static_cast <long double >(sign) * (static_cast <long double >(n) + static_cast <long double >(num)));
390+ }
356391
357392 if (static_cast <long double >(middle_d) * static_cast <long double >(num + err) < static_cast <long double >(middle_n))
358393 {
@@ -369,11 +404,11 @@ std::string dec2frac(double num, const std::string& var, double err)
369404 // Middle is our best fraction
370405 if (n != 0 && middle_d > std::numeric_limits<long long >::max () / std::llabs (n))
371406 {
372- return trimZeros ( std::to_string (sign * (n + num))) + var ;
407+ return decimal_fallback ( static_cast < long double > (sign) * (static_cast < long double >(n) + static_cast < long double >( num)));
373408 }
374409 if (sign != 0 && std::llabs (n * middle_d + middle_n) > std::numeric_limits<long long >::max () / std::llabs (sign))
375410 {
376- return trimZeros ( std::to_string (sign * (n + num))) + var ;
411+ return decimal_fallback ( static_cast < long double > (sign) * (static_cast < long double >(n) + static_cast < long double >( num)));
377412 }
378413 return multiple ((n * middle_d + middle_n) * sign, var) + " /" + std::to_string (middle_d);
379414 }
@@ -382,7 +417,25 @@ std::string dec2frac(double num, const std::string& var, double err)
382417
383418std::string trimZeros (const std::string& str)
384419{
385- return std::to_string (std::stoi (str));
420+ try
421+ {
422+ const long double value = std::stold (str);
423+ if (!std::isfinite (value))
424+ {
425+ return str;
426+ }
427+
428+ std::string out = format_decimal_value (value);
429+ if (out == " -0" )
430+ {
431+ return " 0" ;
432+ }
433+ return out;
434+ }
435+ catch (const std::exception&)
436+ {
437+ return str;
438+ }
386439}
387440
388441std::string entry_name_to_string (const TIVarType& type, const uint8_t * nameBytes, size_t size)
0 commit comments