source: cpp/common/nonstd_math.cpp @ 1139

Last change on this file since 1139 was 1026, checked in by Maciej Komosinski, 4 years ago

Workarounds for Embarcadero 10.3u3 compiler bug and incompatibility

  • Property svn:eol-style set to native
File size: 5.7 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include "nonstd_math.h"
6#include <PrintFloat/PrintFloat.h>
7#include <string.h> // strncpy()
8#include <sstream>
9#include <algorithm> // std::min()
10
11RandomGenerator &rndGetInstance()
12{
13        static RandomGenerator rnd(0);
14        return rnd;
15}
16
17
18std::string doubleToString(double x, int precision) //waiting for a proper, native C++ solution
19{
20        std::stringstream ss; //or for pre-allocated buffer, sprintf(s, "%.*f", precision, x);
21        std::string str;
22        if (fabs(x) < 1e8) //limiting the precision of huge fp values makes little sense - better use scientific notation, unless we want a looong number
23        {
24                ss << std::fixed;
25                ss.precision(precision); //set the number of places after decimal
26                ss << x;
27                str = ss.str();
28                char *s =
29#ifdef __BORLANDC__ //embarcadero 10.3u3 compiler does not support char* str.data() even in C++17 mode?
30                (char*)
31#endif
32                str.data(); //now we will be operating directly on the internal std::string buffer
33                for (int i = str.length() - 1, end = str.length(); i >= 0; i--) //remove trailing zeros, and maybe also '.'
34                {
35                        if (s[i] == '0')
36                        {
37                                if (end == i + 1)
38                                        end = i;
39                        }
40                        else if (s[i] == '.')
41                        {
42                                if (end == i + 1)
43                                        end = i;
44                                s[end] = '\0';
45                                break;
46                        }
47                }
48        }
49        else
50        {
51                ss << x;
52                str = ss.str();
53        }
54        //printf("%.17g and %d decimals: %s\n", x, precision, str.c_str());
55        return str;
56}
57
58int doubleToString(double x, int precision, char *buffer, int bufferlen)
59{
60        // C++ in 2020 and the impossible challenge https://stackoverflow.com/questions/277772/avoid-trailing-zeroes-in-printf
61        if (precision < 0)
62        {
63                // 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".
64#ifdef USE_PRINTFLOAT_DRAGON4
65                return PrintFloat64(buffer, bufferlen, x,
66                        ((x < -1e17) || (x > 1e17) || ((x < 1e-4) && (x > -1e-4) && (x != 0.0)))
67                        ? PrintFloatFormat_Scientific : PrintFloatFormat_Positional,
68                        precision); //http://www.ryanjuckett.com/programming/printing-floating-point-numbers/
69#else
70                return sprintf(buffer, "%.17g", x);
71#endif
72        }
73        else
74        {
75                std::string s = doubleToString(x, precision);
76                strncpy(buffer, s.c_str(), std::min(bufferlen, (int)s.length() + 1));
77                buffer[bufferlen - 1] = 0; //ensure the string is truncated
78                return s.length();
79        }
80}
81
82
83double round(const double x, const int precision)
84{
85        double rounded = std::stod(doubleToString(x, precision));
86        //printf("%d  %20g \t %20g\n", precision, x, rounded); //for debugging
87        return rounded;
88}
89
90
91
92
93#ifdef IPHONE
94//TODO! -> ? http://stackoverflow.com/questions/12762418/how-to-enable-sigfpe-signal-on-division-by-zero-in-ios-app
95void fpExceptInit()
96{}
97
98void fpExceptEnable()
99{}
100
101void fpExceptDisable()
102{}
103#endif
104
105#ifdef MACOS
106//TODO...?
107
108void fpExceptInit()
109{}
110
111void fpExceptEnable()
112{}
113
114void fpExceptDisable()
115{}
116#endif
117
118
119#if defined LINUX || defined TIZEN || defined __ANDROID__
120
121#include <fenv.h>
122
123void fpExceptInit()
124{}
125
126void fpExceptEnable()
127{
128        feclearexcept(FE_DIVBYZERO);
129        feenableexcept(FE_DIVBYZERO);
130}
131
132void fpExceptDisable()
133{
134        fedisableexcept(FE_DIVBYZERO);
135}
136
137#endif
138
139
140
141#ifdef __BORLANDC__
142// there was once a problem like this:
143// http://qc.embarcadero.com/wc/qcmain.aspx?d=5128
144// http://www.delorie.com/djgpp/doc/libc/libc_112.html
145// ? http://www.c-jump.com/CIS77/reference/Intel/CIS77_24319002/pg_0211.htm
146// ? 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
147// ? http://www.plantation-productions.com/Webster/www.artofasm.com/Linux/HTML/RealArithmetica2.html
148// http://blogs.msdn.com/b/oldnewthing/archive/2008/07/03/8682463.aspx
149// where each cast of a double into an int would cause an exception.
150// 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)
151
152#include "log.h"
153
154unsigned int fp_control_word_std;
155unsigned int fp_control_word_muted;
156
157void fpExceptInit()
158{
159        //unsigned int was=_clear87();
160        //logPrintf("","fpExceptInit",LOG_INFO,"control87 status before clear was %08x", was);
161        fp_control_word_std = _control87(0, 0);             //4978 = 1001101110010
162        // Make the new fp env same as the old one, except for the changes we're going to make
163        fp_control_word_muted = fp_control_word_std | EM_INVALID | EM_DENORMAL | EM_ZERODIVIDE | EM_OVERFLOW | EM_UNDERFLOW | EM_INEXACT;  //4991 = 1001101111111
164}
165
166void fpExceptEnable()
167{
168        unsigned int was = _clear87(); //trzeba czyscic zeby nie bylo exception...
169        //logPrintf("","fpExceptEnable ",LOG_INFO,"control87 status before clear was %08x", was);
170        _control87(fp_control_word_std, 0xffffffff);
171        //logPrintf("","fpExceptEnable ",LOG_INFO,"control87 flags are %08x", _control87(0, 0)); //kontrola co sie ustawilo
172}
173
174void fpExceptDisable()
175{
176        unsigned int was = _clear87(); //trzeba czyscic zeby nie bylo exception...
177        //logPrintf("","fpExceptDisable",LOG_INFO,"control87 status before clear was %08x", was);
178        _control87(fp_control_word_muted, 0xffffffff);
179        //logPrintf("","fpExceptDisable",LOG_INFO,"control87 flags are %08x", _control87(0, 0)); //kontrola co sie ustawilo
180}
181
182#endif
183
184
185
186#ifdef _MSC_VER
187//Moznaby zrobic tak jak pod linuxem czyli wlaczyc exceptiony na poczatku i wylaczac na chwile przy dzieleniu w extvalue.
188//To by pozwoli³o na wy³apanie pod visualem z³ych sytuacji kiedy framsy licz¹ na NaN, INF itp.
189//http://stackoverflow.com/questions/2769814/how-do-i-use-try-catch-to-catch-floating-point-errors
190void fpExceptInit() {}
191void fpExceptEnable() {}
192void fpExceptDisable() {}
193#endif
Note: See TracBrowser for help on using the repository browser.