source: cpp/common/nonstd_math.cpp @ 1347

Last change on this file since 1347 was 1347, checked in by Maciej Komosinski, 32 hours ago

Cosmetic

  • Property svn:eol-style set to native
File size: 7.1 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2025  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include "nonstd_math.h"
6#ifdef USE_PRINTFLOAT_DRAGON4
7#include <PrintFloat/PrintFloat.h>
8#endif
9#include <string.h> // strncpy()
10#include <sstream>
11#include <algorithm> // std::min()
12#include <common/log.h>
13
14
15unsigned int rndUint(int limit_exclusive)
16{
17        if (limit_exclusive < 0)
18        {
19                logPrintf("", "rndUint", LOG_ERROR, "rndUint(negative: %d)", limit_exclusive);
20                return 0;
21        }
22        else return rndUint((unsigned int)limit_exclusive);
23}
24
25
26RandomGenerator &rndGetInstance()
27{
28        static RandomGenerator rnd(0);
29        return rnd;
30}
31
32
33std::string doubleToString(double x, int precision) //waiting for a proper, native C++ solution
34{
35        std::stringstream ss; //or for pre-allocated buffer, sprintf(s, "%.*f", precision, x);
36        std::string str;
37        if (fabs(x) < 1e8) //limiting the precision of huge fp values makes little sense - better use scientific notation, unless we want a looong number
38        {
39                ss << std::fixed;
40                ss.precision(precision); //set the number of places after decimal
41                ss << x;
42                str = ss.str();
43                char *s =
44#ifdef __BORLANDC__ //embarcadero 10.3u3 compiler does not support char* str.data() even in C++17 mode?
45                (char*)
46#endif
47                        str.data(); //now we will be operating directly on the internal std::string buffer
48                for (int i = int(str.length()) - 1, end = int(str.length()); i >= 0; i--) //remove trailing zeros, and maybe also '.'
49                {
50                        if (s[i] == '0')
51                        {
52                                if (end == i + 1)
53                                        end = i;
54                        }
55                        else if (s[i] == '.')
56                        {
57                                if (end == i + 1)
58                                        end = i;
59                                s[end] = '\0';
60                                break;
61                        }
62                }
63        }
64        else
65        {
66                ss << x;
67                str = ss.str();
68        }
69        //printf("%.17g and %d decimals: %s\n", x, precision, str.c_str());
70        return str;
71}
72
73int doubleToString(double x, int precision, char *buffer, int bufferlen)
74{
75        // C++ in 2020 and the impossible challenge https://stackoverflow.com/questions/277772/avoid-trailing-zeroes-in-printf
76        if (precision < 0)
77        {
78                // 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".
79#ifdef USE_PRINTFLOAT_DRAGON4
80                return PrintFloat64(buffer, bufferlen, x,
81                        ((x < -1e17) || (x > 1e17) || ((x < 1e-4) && (x > -1e-4) && (x != 0.0)))
82                        ? PrintFloatFormat_Scientific : PrintFloatFormat_Positional,
83                        precision); //http://www.ryanjuckett.com/programming/printing-floating-point-numbers/
84#else
85                return sprintf(buffer, "%.17g", x);
86#endif
87        }
88        else
89        {
90                std::string s = doubleToString(x, precision);
91                strncpy(buffer, s.c_str(), std::min(bufferlen, (int)s.length() + 1));
92                buffer[bufferlen - 1] = 0; //ensure the string is truncated
93                return int(s.length());
94        }
95}
96
97
98double round(const double x, const int precision)
99{
100        double rounded = std::stod(doubleToString(x, precision));
101        //printf("%d  %20g \t %20g\n", precision, x, rounded); //for debugging
102        return rounded;
103}
104
105namespace fpExcept
106{ //shared for all platforms, using crossplatform constants, can be defined in build_config (but also conditionally changed from the code)
107int wanted_exceptions =
108#ifdef WANTED_FP_EXCEPTIONS
109        WANTED_FP_EXCEPTIONS;
110#else
111        FPEX_DIV0; //default when 'WANTED_FP_EXCEPTIONS' is not defined (add more?)
112#endif
113}
114
115// Idea: enable selected floating point exceptions when the app starts and disable them temporarily when dividing values in ExtValue, so that we can directly handle problematic cases there.
116// This allows to catch problematic situations when the program performs calculations using NaN, INF etc.
117
118#ifdef IPHONE
119//TODO! -> ? http://stackoverflow.com/questions/12762418/how-to-enable-sigfpe-signal-on-division-by-zero-in-ios-app
120namespace fpExcept
121{
122        void init() {}
123        void enable() {}
124        void disable() {}
125}
126#endif
127
128#ifdef MACOS
129//TODO...? (even less reasons to omit this in macos :-P)
130namespace fpExcept
131{
132        void init() {}
133        void enable() {}
134        void disable() {}
135}
136#endif
137
138
139#if defined LINUX || defined TIZEN || defined __ANDROID__
140#include <fenv.h>
141
142#ifdef __CYGWIN__ //since my cygwin update in 2024 (g++ (GCC) 11.4.0), these two are no longer found:
143 #define feenableexcept(x)
144 #define fedisableexcept(x)
145#endif
146
147namespace fpExcept
148{
149        void init() {}
150        void enable()
151        {
152                feclearexcept(wanted_exceptions);
153                feenableexcept(wanted_exceptions);
154        }
155        void disable()
156        {
157                fedisableexcept(wanted_exceptions);
158        }
159};
160#endif
161
162
163
164#if defined(__BORLANDC__) || defined(_MSC_VER)
165
166namespace fpExcept
167{
168// in Borland, there was once a problem like this:
169// http://qc.embarcadero.com/wc/qcmain.aspx?d=5128
170// http://www.delorie.com/djgpp/doc/libc/libc_112.html
171// ? http://www.c-jump.com/CIS77/reference/Intel/CIS77_24319002/pg_0211.htm
172// ? 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
173// ? http://www.plantation-productions.com/Webster/www.artofasm.com/Linux/HTML/RealArithmetica2.html
174// http://blogs.msdn.com/b/oldnewthing/archive/2008/07/03/8682463.aspx
175// where each cast of a double into an int would cause an exception.
176// 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)
177
178
179#if defined(__BORLANDC__) // adding a missing constant and a function
180#define _MCW_EM         0x0008001f              // Interrupt Exception Masks - from Visual C++'s float.h
181
182void _controlfp_s(unsigned int* _CurrentState, unsigned int  _NewValue, unsigned int  _Mask) //pretends to be the real _controlfp_s() function
183{
184        *_CurrentState = _control87(_NewValue, _Mask);
185}
186#endif
187
188#if defined(_MSC_VER)
189#pragma fenv_access (on)
190#endif
191
192// http://stackoverflow.com/questions/2769814/how-do-i-use-try-catch-to-catch-floating-point-errors
193
194//#include "log.h"
195
196unsigned int fp_control_word_std;
197unsigned int fp_control_word_muted;
198
199void init()
200{
201        _controlfp_s(&fp_control_word_std, 0, 0); //in Visual C++, the default value is exactly the masks listed below, and we have to turn them off to enable exceptions
202        // Make the new fp env same as the old one, except for the changes we're going to make
203        fp_control_word_muted = fp_control_word_std & ~wanted_exceptions;
204}
205
206void enable()
207{
208        //_fpreset(); //not needed since we just _clearfp()... mentioned in https://stackoverflow.com/questions/4282217/visual-c-weird-behavior-after-enabling-floating-point-exceptions-compiler-b
209        unsigned int was = _clearfp(); //need to clean so that there is no exception...
210        //logPrintf("","fpExceptEnable",LOG_INFO,"control87 status before clear was %08x", was);
211        _controlfp_s(&was, fp_control_word_muted, _MCW_EM);
212}
213
214void disable()
215{
216        //_fpreset(); //not needed since we just _clearfp()... mentioned in https://stackoverflow.com/questions/4282217/visual-c-weird-behavior-after-enabling-floating-point-exceptions-compiler-b
217        unsigned int was = _clearfp(); //need to clean so that there is no exception...
218        //logPrintf("","fpExceptDisable",LOG_INFO,"control87 status before clear was %08x", was);
219        _controlfp_s(&was, fp_control_word_std, _MCW_EM);
220}
221       
222};
223
224#endif
225
Note: See TracBrowser for help on using the repository browser.