// This file is a part of Framsticks SDK. http://www.framsticks.com/ // Copyright (C) 1999-2020 Maciej Komosinski and Szymon Ulatowski. // See LICENSE.txt for details. #include "nonstd_math.h" #include #include // strncpy() #include #include // std::min() RandomGenerator &rndGetInstance() { static RandomGenerator rnd(0); return rnd; } std::string doubleToString(double x, int precision) //waiting for a proper, native C++ solution { std::stringstream ss; //or for pre-allocated buffer, sprintf(s, "%.*f", precision, x); std::string str; if (fabs(x) < 1e8) //limiting the precision of huge fp values makes little sense - better use scientific notation, unless we want a looong number { ss << std::fixed; ss.precision(precision); //set the number of places after decimal ss << x; str = ss.str(); char *s = #ifdef __BORLANDC__ //embarcadero 10.3u3 compiler does not support char* str.data() even in C++17 mode? (char*) #endif str.data(); //now we will be operating directly on the internal std::string buffer for (int i = str.length() - 1, end = str.length(); i >= 0; i--) //remove trailing zeros, and maybe also '.' { if (s[i] == '0') { if (end == i + 1) end = i; } else if (s[i] == '.') { if (end == i + 1) end = i; s[end] = '\0'; break; } } } else { ss << x; str = ss.str(); } //printf("%.17g and %d decimals: %s\n", x, precision, str.c_str()); return str; } int doubleToString(double x, int precision, char *buffer, int bufferlen) { // C++ in 2020 and the impossible challenge https://stackoverflow.com/questions/277772/avoid-trailing-zeroes-in-printf if (precision < 0) { // The "g" format does not allow to use the number of decimal places after the decimal point. Dragon4 on the other hand fills in unnecessary trailinig zeros... so both are good only for "full precision". #ifdef USE_PRINTFLOAT_DRAGON4 return PrintFloat64(buffer, bufferlen, x, ((x < -1e17) || (x > 1e17) || ((x < 1e-4) && (x > -1e-4) && (x != 0.0))) ? PrintFloatFormat_Scientific : PrintFloatFormat_Positional, precision); //http://www.ryanjuckett.com/programming/printing-floating-point-numbers/ #else return sprintf(buffer, "%.17g", x); #endif } else { std::string s = doubleToString(x, precision); strncpy(buffer, s.c_str(), std::min(bufferlen, (int)s.length() + 1)); buffer[bufferlen - 1] = 0; //ensure the string is truncated return s.length(); } } double round(const double x, const int precision) { double rounded = std::stod(doubleToString(x, precision)); //printf("%d %20g \t %20g\n", precision, x, rounded); //for debugging return rounded; } #ifdef IPHONE //TODO! -> ? http://stackoverflow.com/questions/12762418/how-to-enable-sigfpe-signal-on-division-by-zero-in-ios-app void fpExceptInit() {} void fpExceptEnable() {} void fpExceptDisable() {} #endif #ifdef MACOS //TODO...? void fpExceptInit() {} void fpExceptEnable() {} void fpExceptDisable() {} #endif #if defined LINUX || defined TIZEN || defined __ANDROID__ #include void fpExceptInit() {} void fpExceptEnable() { feclearexcept(FE_DIVBYZERO); feenableexcept(FE_DIVBYZERO); } void fpExceptDisable() { fedisableexcept(FE_DIVBYZERO); } #endif #ifdef __BORLANDC__ // there was once a problem like this: // http://qc.embarcadero.com/wc/qcmain.aspx?d=5128 // http://www.delorie.com/djgpp/doc/libc/libc_112.html // ? http://www.c-jump.com/CIS77/reference/Intel/CIS77_24319002/pg_0211.htm // ? http://www.jaist.ac.jp/iscenter-new/mpc/altix/altixdata/opt/intel/vtune/doc/users_guide/mergedProjects/analyzer_ec/mergedProjects/reference_olh/mergedProjects/instructions/instruct32_hh/vc100.htm // ? http://www.plantation-productions.com/Webster/www.artofasm.com/Linux/HTML/RealArithmetica2.html // http://blogs.msdn.com/b/oldnewthing/archive/2008/07/03/8682463.aspx // where each cast of a double into an int would cause an exception. // But it was resolved by restarting windows and cleaning all intermediate compilation files :o (restarting windows was the key element! restarting BC++Builder and deleting files would not help) #include "log.h" unsigned int fp_control_word_std; unsigned int fp_control_word_muted; void fpExceptInit() { //unsigned int was=_clear87(); //logPrintf("","fpExceptInit",LOG_INFO,"control87 status before clear was %08x", was); fp_control_word_std = _control87(0, 0); //4978 = 1001101110010 // Make the new fp env same as the old one, except for the changes we're going to make fp_control_word_muted = fp_control_word_std | EM_INVALID | EM_DENORMAL | EM_ZERODIVIDE | EM_OVERFLOW | EM_UNDERFLOW | EM_INEXACT; //4991 = 1001101111111 } void fpExceptEnable() { unsigned int was = _clear87(); //trzeba czyscic zeby nie bylo exception... //logPrintf("","fpExceptEnable ",LOG_INFO,"control87 status before clear was %08x", was); _control87(fp_control_word_std, 0xffffffff); //logPrintf("","fpExceptEnable ",LOG_INFO,"control87 flags are %08x", _control87(0, 0)); //kontrola co sie ustawilo } void fpExceptDisable() { unsigned int was = _clear87(); //trzeba czyscic zeby nie bylo exception... //logPrintf("","fpExceptDisable",LOG_INFO,"control87 status before clear was %08x", was); _control87(fp_control_word_muted, 0xffffffff); //logPrintf("","fpExceptDisable",LOG_INFO,"control87 flags are %08x", _control87(0, 0)); //kontrola co sie ustawilo } #endif #ifdef _MSC_VER //Moznaby zrobic tak jak pod linuxem czyli wlaczyc exceptiony na poczatku i wylaczac na chwile przy dzieleniu w extvalue. //To by pozwoliło na wyłapanie pod visualem złych sytuacji kiedy framsy liczą na NaN, INF itp. //http://stackoverflow.com/questions/2769814/how-do-i-use-try-catch-to-catch-floating-point-errors void fpExceptInit() {} void fpExceptEnable() {} void fpExceptDisable() {} #endif