source: cpp/common/nonstd_math.cpp @ 1298

Last change on this file since 1298 was 1298, checked in by Maciej Komosinski, 3 weeks ago

Introduced overloads for rndUint() with size_t and int arguments to avoid numerous type casts in sources

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