source: cpp/common/nonstd_math.cpp @ 1001

Last change on this file since 1001 was 1001, checked in by Maciej Komosinski, 2 months ago

Another attempt to rounding floating-point values with desired precision

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