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 }