1 /**
2  * Collects functions for compile-time floating-point calculations.
3  *
4  * Copyright:   Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/ctfloat.d, root/_ctfloat.d)
8  * Documentation: https://dlang.org/phobos/dmd_root_ctfloat.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/ctfloat.d
10  */
11 
12 module dmd.root.ctfloat;
13 
14 static import core.math, core.stdc.math;
15 import core.stdc.errno;
16 import core.stdc.stdio;
17 import core.stdc.stdlib;
18 import core.stdc.string;
19 
20 nothrow:
21 
22 // Type used by the front-end for compile-time reals
23 public import dmd.root.longdouble : real_t = longdouble;
24 
25 private
26 {
27     version(CRuntime_DigitalMars) __gshared extern (C) extern const(char)* __locale_decpoint;
28 
29     version(CRuntime_Microsoft) extern (C++)
30     {
31         public import dmd.root.longdouble : longdouble_soft, ld_sprint;
32         import dmd.root.strtold;
33     }
34 }
35 
36 // Compile-time floating-point helper
37 extern (C++) struct CTFloat
38 {
39   nothrow:
40   @nogc:
41   @safe:
42 
43     version (GNU)
44         enum yl2x_supported = false;
45     else
46         enum yl2x_supported = __traits(compiles, core.math.yl2x(1.0L, 2.0L));
47     enum yl2xp1_supported = yl2x_supported;
48 
49     static void yl2x(const real_t* x, const real_t* y, real_t* res) pure
50     {
51         static if (yl2x_supported)
52             *res = core.math.yl2x(*x, *y);
53         else
54             assert(0);
55     }
56 
57     static void yl2xp1(const real_t* x, const real_t* y, real_t* res) pure
58     {
59         static if (yl2xp1_supported)
60             *res = core.math.yl2xp1(*x, *y);
61         else
62             assert(0);
63     }
64 
65     static if (!is(real_t == real))
66     {
67         alias sin = dmd.root.longdouble.sinl;
68         alias cos = dmd.root.longdouble.cosl;
69         alias tan = dmd.root.longdouble.tanl;
70         alias sqrt = dmd.root.longdouble.sqrtl;
71         alias fabs = dmd.root.longdouble.fabsl;
72         alias ldexp = dmd.root.longdouble.ldexpl;
73     }
74     else
75     {
76         pure static real_t sin(real_t x) { return core.math.sin(x); }
77         pure static real_t cos(real_t x) { return core.math.cos(x); }
78         static real_t tan(real_t x) { return core.stdc.math.tanl(x); }
79         pure static real_t sqrt(real_t x) { return core.math.sqrt(x); }
80         pure static real_t fabs(real_t x) { return core.math.fabs(x); }
81         pure static real_t ldexp(real_t n, int exp) { return core.math.ldexp(n, exp); }
82     }
83 
84     static if (!is(real_t == real))
85     {
86         static real_t round(real_t x) { return real_t(cast(double)core.stdc.math.roundl(cast(double)x)); }
87         static real_t floor(real_t x) { return real_t(cast(double)core.stdc.math.floor(cast(double)x)); }
88         static real_t ceil(real_t x) { return real_t(cast(double)core.stdc.math.ceil(cast(double)x)); }
89         static real_t trunc(real_t x) { return real_t(cast(double)core.stdc.math.trunc(cast(double)x)); }
90         static real_t log(real_t x) { return real_t(cast(double)core.stdc.math.logl(cast(double)x)); }
91         static real_t log2(real_t x) { return real_t(cast(double)core.stdc.math.log2l(cast(double)x)); }
92         static real_t log10(real_t x) { return real_t(cast(double)core.stdc.math.log10l(cast(double)x)); }
93         static real_t pow(real_t x, real_t y) { return real_t(cast(double)core.stdc.math.powl(cast(double)x, cast(double)y)); }
94         static real_t exp(real_t x) { return real_t(cast(double)core.stdc.math.expl(cast(double)x)); }
95         static real_t expm1(real_t x) { return real_t(cast(double)core.stdc.math.expm1l(cast(double)x)); }
96         static real_t exp2(real_t x) { return real_t(cast(double)core.stdc.math.exp2l(cast(double)x)); }
97         static real_t copysign(real_t x, real_t s) { return real_t(cast(double)core.stdc.math.copysignl(cast(double)x, cast(double)s)); }
98     }
99     else
100     {
101         static real_t round(real_t x) { return core.stdc.math.roundl(x); }
102         static real_t floor(real_t x) { return core.stdc.math.floor(x); }
103         static real_t ceil(real_t x) { return core.stdc.math.ceil(x); }
104         static real_t trunc(real_t x) { return core.stdc.math.trunc(x); }
105         static real_t log(real_t x) { return core.stdc.math.logl(x); }
106         static real_t log2(real_t x) { return core.stdc.math.log2l(x); }
107         static real_t log10(real_t x) { return core.stdc.math.log10l(x); }
108         static real_t pow(real_t x, real_t y) { return core.stdc.math.powl(x, y); }
109         static real_t exp(real_t x) { return core.stdc.math.expl(x); }
110         static real_t expm1(real_t x) { return core.stdc.math.expm1l(x); }
111         static real_t exp2(real_t x) { return core.stdc.math.exp2l(x); }
112         static real_t copysign(real_t x, real_t s) { return core.stdc.math.copysignl(x, s); }
113     }
114 
115     pure
116     static real_t fmin(real_t x, real_t y) { return x < y ? x : y; }
117     pure
118     static real_t fmax(real_t x, real_t y) { return x > y ? x : y; }
119 
120     pure
121     static real_t fma(real_t x, real_t y, real_t z) { return (x * y) + z; }
122 
123     pure @trusted
124     static bool isIdentical(real_t a, real_t b)
125     {
126         // don't compare pad bytes in extended precision
127         enum sz = (real_t.mant_dig == 64) ? 10 : real_t.sizeof;
128         return memcmp(&a, &b, sz) == 0;
129     }
130 
131     pure @trusted
132     static size_t hash(real_t a)
133     {
134         import dmd.root.hash : calcHash;
135 
136         if (isNaN(a))
137             a = real_t.nan;
138         enum sz = (real_t.mant_dig == 64) ? 10 : real_t.sizeof;
139         return calcHash((cast(ubyte*) &a)[0 .. sz]);
140     }
141 
142     pure
143     static bool isNaN(real_t r)
144     {
145         return !(r == r);
146     }
147 
148     pure @trusted
149     static bool isSNaN(real_t r)
150     {
151         return isNaN(r) && !(((cast(ubyte*)&r)[7]) & 0x40);
152     }
153 
154     // the implementation of longdouble for MSVC is a struct, so mangling
155     //  doesn't match with the C++ header.
156     // add a wrapper just for isSNaN as this is the only function called from C++
157     version(CRuntime_Microsoft) static if (is(real_t == real))
158         pure @trusted
159         static bool isSNaN(longdouble_soft ld)
160         {
161             return isSNaN(cast(real)ld);
162         }
163 
164     static bool isInfinity(real_t r) pure
165     {
166         return isIdentical(fabs(r), real_t.infinity);
167     }
168 
169     @system
170     static real_t parse(const(char)* literal, bool* isOutOfRange = null)
171     {
172         errno = 0;
173         version(CRuntime_DigitalMars)
174         {
175             auto save = __locale_decpoint;
176             __locale_decpoint = ".";
177         }
178         version(CRuntime_Microsoft)
179         {
180             auto r = cast(real_t) strtold_dm(literal, null);
181         }
182         else
183             auto r = strtold(literal, null);
184         version(CRuntime_DigitalMars) __locale_decpoint = save;
185         if (isOutOfRange)
186             *isOutOfRange = (errno == ERANGE);
187         return r;
188     }
189 
190     @system
191     static int sprint(char* str, char fmt, real_t x)
192     {
193         version(CRuntime_Microsoft)
194         {
195             auto len = cast(int) ld_sprint(str, fmt, longdouble_soft(x));
196         }
197         else
198         {
199             char[4] sfmt = "%Lg\0";
200             sfmt[2] = fmt;
201             auto len = sprintf(str, sfmt.ptr, x);
202         }
203 
204         if (fmt != 'a' && fmt != 'A')
205         {
206             assert(fmt == 'g');
207 
208             // 1 => 1.0 to distinguish from integers
209             bool needsFPSuffix = true;
210             foreach (char c; str[0 .. len])
211             {
212                 // str might be `nan` or `inf`...
213                 if (c != '-' && !(c >= '0' && c <= '9'))
214                 {
215                     needsFPSuffix = false;
216                     break;
217                 }
218             }
219 
220             if (needsFPSuffix)
221             {
222                 str[len .. len+3] = ".0\0";
223                 len += 2;
224             }
225         }
226 
227         return len;
228     }
229 
230     // Constant real values 0, 1, -1 and 0.5.
231     __gshared real_t zero;
232     __gshared real_t one;
233     __gshared real_t minusone;
234     __gshared real_t half;
235 
236     @trusted
237     static void initialize()
238     {
239         zero = real_t(0);
240         one = real_t(1);
241         minusone = real_t(-1);
242         half = real_t(0.5);
243     }
244 }