1 /**
2  * Collects functions for compile-time floating-point calculations.
3  *
4  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 https://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         static import dmd.root.longdouble;
68         alias sin = dmd.root.longdouble.sinl;
69         alias cos = dmd.root.longdouble.cosl;
70         alias tan = dmd.root.longdouble.tanl;
71         alias sqrt = dmd.root.longdouble.sqrtl;
72         alias fabs = dmd.root.longdouble.fabsl;
73         alias ldexp = dmd.root.longdouble.ldexpl;
74     }
75     else
76     {
77         pure static real_t sin(real_t x) { return core.math.sin(x); }
78         pure static real_t cos(real_t x) { return core.math.cos(x); }
79         static real_t tan(real_t x) { return core.stdc.math.tanl(x); }
80         pure static real_t sqrt(real_t x) { return core.math.sqrt(x); }
81         pure static real_t fabs(real_t x) { return core.math.fabs(x); }
82         pure static real_t ldexp(real_t n, int exp) { return core.math.ldexp(n, exp); }
83     }
84 
85     static if (!is(real_t == real))
86     {
87         static real_t round(real_t x) { return real_t(cast(double)core.stdc.math.roundl(cast(double)x)); }
88         static real_t floor(real_t x) { return real_t(cast(double)core.stdc.math.floor(cast(double)x)); }
89         static real_t ceil(real_t x) { return real_t(cast(double)core.stdc.math.ceil(cast(double)x)); }
90         static real_t trunc(real_t x) { return real_t(cast(double)core.stdc.math.trunc(cast(double)x)); }
91         static real_t log(real_t x) { return real_t(cast(double)core.stdc.math.logl(cast(double)x)); }
92         static real_t log2(real_t x) { return real_t(cast(double)core.stdc.math.log2l(cast(double)x)); }
93         static real_t log10(real_t x) { return real_t(cast(double)core.stdc.math.log10l(cast(double)x)); }
94         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)); }
95         static real_t exp(real_t x) { return real_t(cast(double)core.stdc.math.expl(cast(double)x)); }
96         static real_t expm1(real_t x) { return real_t(cast(double)core.stdc.math.expm1l(cast(double)x)); }
97         static real_t exp2(real_t x) { return real_t(cast(double)core.stdc.math.exp2l(cast(double)x)); }
98         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)); }
99     }
100     else
101     {
102         static real_t round(real_t x) { return core.stdc.math.roundl(x); }
103         static real_t floor(real_t x) { return core.stdc.math.floor(x); }
104         static real_t ceil(real_t x) { return core.stdc.math.ceil(x); }
105         static real_t trunc(real_t x) { return core.stdc.math.trunc(x); }
106         static real_t log(real_t x) { return core.stdc.math.logl(x); }
107         static real_t log2(real_t x) { return core.stdc.math.log2l(x); }
108         static real_t log10(real_t x) { return core.stdc.math.log10l(x); }
109         static real_t pow(real_t x, real_t y) { return core.stdc.math.powl(x, y); }
110         static real_t exp(real_t x) { return core.stdc.math.expl(x); }
111         static real_t expm1(real_t x) { return core.stdc.math.expm1l(x); }
112         static real_t exp2(real_t x) { return core.stdc.math.exp2l(x); }
113         static real_t copysign(real_t x, real_t s) { return core.stdc.math.copysignl(x, s); }
114     }
115 
116     pure
117     static real_t fmin(real_t x, real_t y) { return x < y ? x : y; }
118     pure
119     static real_t fmax(real_t x, real_t y) { return x > y ? x : y; }
120 
121     pure
122     static real_t fma(real_t x, real_t y, real_t z) { return (x * y) + z; }
123 
124     pure @trusted
125     static bool isIdentical(real_t a, real_t b)
126     {
127         // don't compare pad bytes in extended precision
128         enum sz = (real_t.mant_dig == 64) ? 10 : real_t.sizeof;
129         return memcmp(&a, &b, sz) == 0;
130     }
131 
132     pure @trusted
133     static size_t hash(real_t a)
134     {
135         import dmd.root.hash : calcHash;
136 
137         if (isNaN(a))
138             a = real_t.nan;
139         enum sz = (real_t.mant_dig == 64) ? 10 : real_t.sizeof;
140         return calcHash((cast(ubyte*) &a)[0 .. sz]);
141     }
142 
143     pure
144     static bool isNaN(real_t r)
145     {
146         return !(r == r);
147     }
148 
149     pure @trusted
150     static bool isSNaN(real_t r)
151     {
152         return isNaN(r) && !(((cast(ubyte*)&r)[7]) & 0x40);
153     }
154 
155     // the implementation of longdouble for MSVC is a struct, so mangling
156     //  doesn't match with the C++ header.
157     // add a wrapper just for isSNaN as this is the only function called from C++
158     version(CRuntime_Microsoft) static if (is(real_t == real))
159         pure @trusted
160         static bool isSNaN(longdouble_soft ld)
161         {
162             return isSNaN(cast(real)ld);
163         }
164 
165     static bool isInfinity(real_t r) pure
166     {
167         return isIdentical(fabs(r), real_t.infinity);
168     }
169 
170     @system
171     static real_t parse(const(char)* literal, out bool isOutOfRange)
172     {
173         errno = 0;
174         version(CRuntime_DigitalMars)
175         {
176             auto save = __locale_decpoint;
177             __locale_decpoint = ".";
178         }
179         version(CRuntime_Microsoft)
180         {
181             auto r = cast(real_t) strtold_dm(literal, null);
182         }
183         else
184             auto r = strtold(literal, null);
185         version(CRuntime_DigitalMars) __locale_decpoint = save;
186         isOutOfRange = (errno == ERANGE);
187         return r;
188     }
189 
190     @system
191     static int sprint(char* str, size_t size, char fmt, real_t x)
192     {
193         version(CRuntime_Microsoft)
194         {
195             auto len = cast(int) ld_sprint(str, size, fmt, longdouble_soft(x));
196         }
197         else
198         {
199             char[4] sfmt = "%Lg\0";
200             sfmt[2] = fmt;
201             auto len = snprintf(str, size, 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 }