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 }