source: cpp/common/nonstd_math.cpp @ 979

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

Improved float-rounding functions, follow-up to r970

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