Created
March 17, 2022 22:29
-
-
Save matu3ba/040ef3ad23f53213ad277999eb847586 to your computer and use it in GitHub Desktop.
prase_float128
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/lib/std/fmt/parse_float.zig b/lib/std/fmt/parse_float.zig | |
index 585c23ed5..585400587 100644 | |
--- a/lib/std/fmt/parse_float.zig | |
+++ b/lib/std/fmt/parse_float.zig | |
@@ -1,36 +1,1111 @@ | |
-// Adapted from https://github.com/grzegorz-kraszewski/stringtofloat. | |
+// Code ported from musl libc 8f12c4e110acb3bbbdc8abfb3a552c3ced718039 | |
+// and then modified to use softfloat and to assume f128 for everything | |
-// MIT License | |
-// | |
-// Copyright (c) 2016 Grzegorz Kraszewski | |
-// | |
-// Permission is hereby granted, free of charge, to any person obtaining a copy | |
-// of this software and associated documentation files (the "Software"), to deal | |
-// in the Software without restriction, including without limitation the rights | |
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
-// copies of the Software, and to permit persons to whom the Software is | |
-// furnished to do so, subject to the following conditions: | |
-// | |
-// The above copyright notice and this permission notice shall be included in all | |
-// copies or substantial portions of the Software. | |
-// | |
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
-// SOFTWARE. | |
-// | |
+const std = @import("std"); | |
+const builtin = @import("builtin"); | |
+const ascii = std.ascii; | |
+const endian = builtin.target.cpu.arch.endian(); | |
-// Be aware that this implementation has the following limitations: | |
+// TODO port | |
+//#define shcnt(f) ((f)->shcnt + ((f)->rpos - (f)->buf)) | |
+//#define shlim(f, lim) __shlim((f), (lim)) | |
+//#define shgetc(f) (((f)->rpos != (f)->shend) ? *(f)->rpos++ : __shgetc(f)) | |
+//#define shunget(f) ((f)->shlim>=0 ? (void)(f)->rpos-- : (void)0) | |
// | |
-// - Is not round-trip accurate for all values | |
-// - Only supports round-to-zero | |
-// - Does not handle denormals | |
+//#define sh_fromstring(f, s) \ | |
+// ((f)->buf = (f)->rpos = (void *)(s), (f)->rend = (void*)-1) | |
-const std = @import("std"); | |
-const ascii = std.ascii; | |
+const LD_B1B_DIG = 4; | |
+const KMAX = 2048; | |
+ | |
+const MASK = (KMAX - 1); | |
+ | |
+const F_PERM = 1; | |
+const F_NORD = 4; | |
+const F_NOWR = 8; | |
+const F_EOF = 16; | |
+const F_ERR = 32; | |
+const F_SVB = 64; | |
+const F_APP = 128; | |
+ | |
+const EOF = -1; | |
+ | |
+const LDBL_MANT_DIG = 113; | |
+const LDBL_MIN_EXP = -16381; | |
+const LDBL_MAX_EXP = 16384; | |
+const LDBL_DIG = 33; | |
+const LDBL_MIN_10_EXP = -4931; | |
+const LDBL_MAX_10_EXP = 4932; | |
+const DECIMAL_DIG = 36; | |
+ | |
+const LdshapeLittle = union { | |
+ f: f128, | |
+ i: struct { | |
+ lo: u64, | |
+ mid: u32, | |
+ top: u16, | |
+ se: u16, | |
+ }, | |
+ i2: struct { | |
+ lo: u64, | |
+ hi: u64, | |
+ }, | |
+}; | |
+ | |
+const LdshapeBig = union { | |
+ f: f128, | |
+ i: struct { | |
+ se: u16, | |
+ top: u16, | |
+ mid: u32, | |
+ lo: u64, | |
+ }, | |
+ @"i2": struct { | |
+ hi: u64, | |
+ lo: u64, | |
+ }, | |
+}; | |
+ | |
+const Ldshape = switch (endian) { | |
+ .Little => LdshapeLittle, | |
+ .Big => LdshapeBig, | |
+}; | |
+ | |
+const MuslFILE = struct { | |
+ flags: u16, // unsigned | |
+ rpos: *u8, // unsigned char* | |
+ rend: *u8, // unsigned char* | |
+ wend: *u8, // unsigned char* | |
+ wpos: *u8, // unsigned char* | |
+ wbase: *u8, // unsigned char* | |
+ buf: *u8, // unsigned char* | |
+ buf_size: usize, // size_t | |
+ mode: i32, // int | |
+ shend: *u8, // unsigned char* | |
+ shlim: isize, // off_t | |
+ shcnt: isize, // off_t | |
+}; | |
+ | |
+fn shlimHelp(f: *MuslFILE, lim: isize) void { | |
+ f.shlim = lim; | |
+ f.shcnt = f.buf - f.rpos; | |
+ // If lim is nonzero, rend must be a valid pointer. | |
+ if (lim and f.rend - f.rpos > lim) { | |
+ f.shend = f.rpos + lim; | |
+ } else { | |
+ f.shend = f.rend; | |
+ } | |
+} | |
+ | |
+fn toReadHelp(f: *MuslFILE) i32 { | |
+ f.mode |= f.mode - 1; | |
+ if (f.wpos != f.wbase) f.write(f, 0, 0); // TODO fix this | |
+ f.wpos = 0; | |
+ f.wbase = 0; | |
+ f.wend = 0; | |
+ if (f.flags & F_NORD) { | |
+ f.flags |= F_ERR; | |
+ return EOF; | |
+ } | |
+ f.rend = f.buf + f.buf_size; | |
+ f.rpos = f.rend; | |
+ if (f.flags & F_EOF > 0) { | |
+ return EOF; | |
+ } else { | |
+ return 0; | |
+ } | |
+} | |
+ | |
+fn uflow(f: *MuslFILE) i32 { | |
+ var c: u8 = undefined; | |
+ if (!toReadHelp(f) and f.read(f, &c, 1) == 1) | |
+ return c; | |
+ return EOF; | |
+} | |
+ | |
+// TODO below | |
+fn shgetc(f: *MuslFILE) i32 { | |
+ var c: i32 = undefined; | |
+ var cnt: isize = shcnt(f); | |
+ // TODO is it possible to further simplify the following statement? | |
+ // if ((f->shlim && cnt >= f->shlim) || (c=__uflow(f)) < 0) { | |
+ if(f.shlim and cnt >= f.shlim) { | |
+ f.shcnt = f.buf - f.rpos + cnt; | |
+ f.shend = f.rpos; | |
+ f.shlim = -1; | |
+ return EOF; | |
+ | |
+ } else { | |
+ c = uflow(f); | |
+ if(c<0) { | |
+ f.shcnt = f.buf - f.rpos + cnt; | |
+ f.shend = f.rpos; | |
+ f.shlim = -1; | |
+ return EOF; | |
+ } | |
+ } | |
+ cnt+=1; | |
+ if (f.shlim && f.rend - f.rpos > f.shlim - cnt) { | |
+ f.shend = f.rpos + (f.shlim - cnt); | |
+ } else { | |
+ f.shend = f.rend; | |
+ } | |
+ f.shcnt = f.buf - f.rpos + cnt; | |
+ if (f.rpos[-1] != c) f.rpos[-1] = c; | |
+ return c; | |
+} | |
+ | |
+fn scanexp(f: *MuslFILE, pok: i32) i64 { | |
+ var c: i32 = undefined; | |
+ var x: i32 = undefined; | |
+ var y: i64 = undefined; | |
+ var neg: i32 = 0; | |
+ | |
+ c = shgetc(f); | |
+ if (c=='+' or c=='-') { | |
+ neg = (c=='-'); | |
+ c = shgetc(f); | |
+ if (c-'0'>=10 and pok) // 10U | |
+ shunget(f); | |
+ } | |
+ if (c-'0'>=10 and c!='_') { //10U | |
+ shunget(f); | |
+ return LLONG_MIN; | |
+ } | |
+ x = 0; | |
+ while(true) { | |
+ c = shgetc(f); | |
+ if (c=='_') { | |
+ continue; | |
+ } else if (c-'0'<10 and x<INT_MAX/10) { // 10U | |
+ x = 10*x + c-'0'; | |
+ } else { | |
+ break; | |
+ } | |
+ } | |
+ y=x; | |
+ while(true) { | |
+ c = shgetc(f); | |
+ if (c=='_') { | |
+ continue; | |
+ } else if (c-'0'<10 and y<LLONG_MAX/100) { // 10U | |
+ y = 10*y + c-'0'; | |
+ } else { | |
+ break; | |
+ } | |
+ } | |
+ while(c-'0'<10 or c=='_') { // 10U | |
+ c = shgetc(f); | |
+ } | |
+ shunget(f); | |
+ if (neg != 0) { // return neg ? -y : y; | |
+ return -y; | |
+ }else { | |
+ return y; | |
+ } | |
+} | |
+ | |
+fn copysignf128(x: f128, y: f128) f128 { | |
+{ | |
+ var ux = Ldshape { | |
+ .f = x, | |
+ }; | |
+ var uy = Ldshape { | |
+ .f = y, | |
+ }; | |
+ ux.i.se &= 0x7fff; | |
+ ux.i.se |= uy.i.se & 0x8000; | |
+ return ux.f; | |
+} | |
+ | |
+fn mul_eq_f128_float(x: *f128, op_float: f32) void { // float op_float | |
+ //x *= 0x1p120f; | |
+ var op_f32: f32 = undefined; | |
+ memcpy(&op_f32, &op_float, sizeof(float)); // FIXME | |
+ var op_f128: f128 = undefined; | |
+ f32_to_f128M(op_f32, &op_f128); // FIXME | |
+ var new_value: f128 = undefined; | |
+ f128M_mul(x, &op_f128, &new_value); // FIXME | |
+ x.* = new_value; | |
+} | |
+ | |
+fn dbl_to_f128(x: f64) f128 { | |
+ var x_f64: f64; | |
+ memcpy(&x_f64, &x, sizeof(double)); // FIXME | |
+ var result: f128 = undefined; | |
+ f64_to_f128M(x_f64, &result); // FIXME | |
+ return result; | |
+} | |
+ | |
+fn fmodf128(x: f128, y: f128) f128 | |
+{ | |
+ var ux = Ldshape { | |
+ .f = x, | |
+ }; | |
+ var uy = Ldshape { | |
+ .f = y, | |
+ }; | |
+ var ex: i32 = ux.i.se & 0x7fff; | |
+ var ey: i32 = uy.i.se & 0x7fff; | |
+ var sx: i32 = ux.i.se & 0x8000; | |
+ | |
+ var zero: f128; | |
+ ui32_to_f128M(0, &zero); // FIXME | |
+ // if (y == 0 || isnan(y) || ex == 0x7fff) | |
+ if (f128M_eq(&y, &zero) || f128M_isSignalingNaN(&y) || ex == 0x7fff) { // FIXME | |
+ //return (x*y)/(x*y); | |
+ var x_times_y: f128; | |
+ f128M_mul(&x, &y, &x_times_y); // FIXME | |
+ var result: f128; | |
+ f128M_div(&x_times_y, &x_times_y, &result); // FIXME | |
+ return result; | |
+ } | |
+ ux.i.se = ex; | |
+ uy.i.se = ey; | |
+ //if (ux.f <= uy.f) { | |
+ if (f128M_le(&ux.f, &uy.f)) { | |
+ //if (ux.f == uy.f) { | |
+ if (f128M_eq(&ux.f, &uy.f)) { | |
+ //return 0*x; | |
+ var result: f128; | |
+ f128M_mul(&zero, &x, &result); // FIXME | |
+ return result; | |
+ } | |
+ return x; | |
+ } | |
+ | |
+ // normalize x and y | |
+ if (!ex) { | |
+ //ux.f *= 0x1p120f; | |
+ mul_eq_f128_float(&ux.f, 0x1p120); // 0x1p120f | |
+ ex = ux.i.se - 120; | |
+ } | |
+ if (!ey) { | |
+ //uy.f *= 0x1p120f; | |
+ mul_eq_f128_float(&uy.f, 0x1p120); // 0x1p120f | |
+ ey = uy.i.se - 120; | |
+ } | |
+ | |
+ // x mod y | |
+ var hi: u64 = undefined; | |
+ var lo: u64 = undefined; | |
+ var xhi: u64 = undefined; | |
+ var xlo: u64 = undefined; | |
+ var yhi: u64 = undefined; | |
+ var ylo: u64 = undefined; | |
+ // FIXME: what does -1ULL>>16 evaluate to??? is this UB? | |
+ xhi = (ux.i2.hi & -1ULL>>16) | @as(u64,1)<<48; // -1ULL, 1ULL | |
+ yhi = (uy.i2.hi & -1ULL>>16) | @as(u64,1)<<48; // -1ULL, 1ULL | |
+ xlo = ux.i2.lo; | |
+ ylo = uy.i2.lo; | |
+ while(ex > ey) : (ex-=1) { | |
+ hi = xhi - yhi; | |
+ lo = xlo - ylo; | |
+ if (xlo < ylo) | |
+ hi -= 1; | |
+ if (hi >> 63 == 0) { | |
+ if ((hi|lo) == 0) { | |
+ //return 0*x; | |
+ var result: f128= undefined; | |
+ f128M_mul(&zero, &x, &result); | |
+ return result; | |
+ } | |
+ xhi = 2*hi + (lo>>63); | |
+ xlo = 2*lo; | |
+ } else { | |
+ xhi = 2*xhi + (xlo>>63); | |
+ xlo = 2*xlo; | |
+ } | |
+ } | |
+ hi = xhi - yhi; | |
+ lo = xlo - ylo; | |
+ if (xlo < ylo) | |
+ hi -= 1; | |
+ if (hi >> 63 == 0) { | |
+ if ((hi|lo) == 0) { | |
+ //return 0*x; | |
+ var result: f128 = undefined; | |
+ f128M_mul(&zero, &x, &result); | |
+ return result; | |
+ } | |
+ xhi = hi; | |
+ xlo = lo; | |
+ } | |
+ for (; xhi >> 48 == 0; xhi = 2*xhi + (xlo>>63), xlo = 2*xlo, ex--); | |
+ ux.i2.hi = xhi; | |
+ ux.i2.lo = xlo; | |
+ | |
+ /* scale result */ | |
+ if (ex <= 0) { | |
+ ux.i.se = (ex+120)|sx; | |
+ //ux.f *= 0x1p-120f; | |
+ mul_eq_f128_float(&ux.f, 0x1p-120f); | |
+ } else | |
+ ux.i.se = ex|sx; | |
+ return ux.f; | |
+} | |
+ | |
+static float128_t int_mul_f128_cast_u32(int sign, uint32_t x0) { | |
+ var x0_f128: f128 = undefined; | |
+ ui32_to_f128M(x0, &x0_f128); | |
+ var sign_f128: f128 = undefined; | |
+ i32_to_f128M(sign, &sign_f128); | |
+ var result; | |
+ f128M_mul(&sign_f128, &x0_f128, &result); | |
+ return result; | |
+} | |
+ | |
+static float128_t triple_divide(int sign, uint32_t x0, int p10s) { | |
+ var part1: f128 = int_mul_f128_cast_u32(sign, x0); | |
+ var p10s_f128: f128 = undefined; | |
+ i32_to_f128M(p10s, &p10s_f128); | |
+ var result: f128 = undefined; | |
+ f128M_div(&part1, &p10s_f128, &result); | |
+ return result; | |
+} | |
+ | |
+static float128_t triple_multiply(int sign, uint32_t x0, int p10s) { | |
+ var part1:f128 = int_mul_f128_cast_u32(sign, x0); | |
+ var p10s_f128: f128 = undefined; | |
+ i32_to_f128M(p10s, &p10s_f128); | |
+ var result: f128 = undefined; | |
+ f128M_mul(&part1, &p10s_f128, &result); | |
+ return result; | |
+} | |
+ | |
+fn void mul_eq_f128_int(float128_t *y, int sign) { | |
+ var sign_f128: f128 = undefined; | |
+ i32_to_f128M(sign, &sign_f128); | |
+ var new_value: f128 = undefined; | |
+ f128M_mul(y, &sign_f128, &new_value); | |
+ *y = new_value; | |
+} | |
+ | |
+fn make_f128(uint64_t hi, uint64_t lo) f128 { | |
+ const ldshape = Ldshape { | |
+ .@"i2".hi = hi; | |
+ .@"i2".lo = lo; | |
+ }; | |
+ return ldshape.f; | |
+} | |
+ | |
+static void mul_eq_f128_f128(float128_t *a, float128_t b) { | |
+ var new_value: f128 = undefined; | |
+ f128M_mul(a, &b, &new_value); | |
+ *a = new_value; | |
+} | |
+ | |
+static void add_eq_f128_dbl(float128_t *a, double b) { | |
+ float64_t b_f64; | |
+ memcpy(&b_f64, &b, sizeof(double)); | |
+ | |
+ var b_f128: f128 = undefined; | |
+ f64_to_f128M(b_f64, &b_f128); | |
+ | |
+ var new_value: f128 = undefined; | |
+ f128M_add(a, &b_f128, &new_value); | |
+ *a = new_value; | |
+} | |
+ | |
+static float128_t scalbnf128(float128_t x, int n) | |
+{ | |
+ union ldshape u; | |
+ | |
+ if (n > 16383) { | |
+ //x *= 0x1p16383q; | |
+ mul_eq_f128_f128(&x, make_f128(0x7ffe000000000000, 0x0000000000000000)); | |
+ n -= 16383; | |
+ if (n > 16383) { | |
+ //x *= 0x1p16383q; | |
+ mul_eq_f128_f128(&x, make_f128(0x7ffe000000000000, 0x0000000000000000)); | |
+ n -= 16383; | |
+ if (n > 16383) | |
+ n = 16383; | |
+ } | |
+ } else if (n < -16382) { | |
+ //x *= 0x1p-16382q * 0x1p113q; | |
+ { | |
+ var mul_result: f128 = undefined; | |
+ const a: f128 = make_f128(0x0001000000000000, 0x0000000000000000); | |
+ const b: f128 = make_f128(0x4070000000000000, 0x0000000000000000); | |
+ f128M_mul(&a, &b, &mul_result); | |
+ mul_eq_f128_f128(&x, mul_result); | |
+ } | |
+ n += 16382 - 113; | |
+ if (n < -16382) { | |
+ //x *= 0x1p-16382q * 0x1p113q; | |
+ { | |
+ var mul_result: f128 = undefined; | |
+ var a: f128 = make_f128(0x0001000000000000, 0x0000000000000000); | |
+ var b: f128 = make_f128(0x4070000000000000, 0x0000000000000000); | |
+ f128M_mul(&a, &b, &mul_result); | |
+ mul_eq_f128_f128(&x, mul_result); | |
+ } | |
+ n += 16382 - 113; | |
+ if (n < -16382) | |
+ n = -16382; | |
+ } | |
+ } | |
+ //u.f = 1.0; | |
+ ui32_to_f128M(1, &u.f); | |
+ u.i.se = 0x3fff + n; | |
+ mul_eq_f128_f128(&x, u.f); | |
+ return x; | |
+} | |
+ | |
+static float128_t fabsf128(float128_t x) | |
+{ | |
+ union ldshape u = {x}; | |
+ | |
+ u.i.se &= 0x7fff; | |
+ return u.f; | |
+} | |
+ | |
+static float128_t decfloat(struct MuslFILE *f, int c, int bits, int emin, int sign, int pok) | |
+{ | |
+ uint32_t x[KMAX]; | |
+ static const uint32_t th[] = { LD_B1B_MAX }; | |
+ int i, j, k, a, z; | |
+ long long lrp=0, dc=0; | |
+ long long e10=0; | |
+ int lnz = 0; | |
+ int gotdig = 0, gotrad = 0; | |
+ int rp; | |
+ int e2; | |
+ int emax = -emin-bits+3; | |
+ int denormal = 0; | |
+ float128_t y; | |
+ float128_t zero; | |
+ ui32_to_f128M(0, &zero); | |
+ float128_t frac=zero; | |
+ float128_t bias=zero; | |
+ static const int p10s[] = { 10, 100, 1000, 10000, | |
+ 100000, 1000000, 10000000, 100000000 }; | |
+ | |
+ j=0; | |
+ k=0; | |
+ | |
+ /* Don't let leading zeros/underscores consume buffer space */ | |
+ for (; ; c = shgetc(f)) { | |
+ if (c=='_') { | |
+ continue; | |
+ } else if (c=='0') { | |
+ gotdig=1; | |
+ } else { | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (c=='.') { | |
+ gotrad = 1; | |
+ for (c = shgetc(f); ; c = shgetc(f)) { | |
+ if (c == '_') { | |
+ continue; | |
+ } else if (c=='0') { | |
+ gotdig=1; | |
+ lrp--; | |
+ } else { | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ | |
+ x[0] = 0; | |
+ for (; c-'0'<10U || c=='.' || c=='_'; c = shgetc(f)) { | |
+ if (c == '_') { | |
+ continue; | |
+ } else if (c == '.') { | |
+ if (gotrad) break; | |
+ gotrad = 1; | |
+ lrp = dc; | |
+ } else if (k < KMAX-3) { | |
+ dc++; | |
+ if (c!='0') lnz = dc; | |
+ if (j) x[k] = x[k]*10 + c-'0'; | |
+ else x[k] = c-'0'; | |
+ if (++j==9) { | |
+ k++; | |
+ j=0; | |
+ } | |
+ gotdig=1; | |
+ } else { | |
+ dc++; | |
+ if (c!='0') { | |
+ lnz = (KMAX-4)*9; | |
+ x[KMAX-4] |= 1; | |
+ } | |
+ } | |
+ } | |
+ if (!gotrad) lrp=dc; | |
+ | |
+ if (gotdig && (c|32)=='e') { | |
+ e10 = scanexp(f, pok); | |
+ if (e10 == LLONG_MIN) { | |
+ if (pok) { | |
+ shunget(f); | |
+ } else { | |
+ shlim(f, 0); | |
+ return zero; | |
+ } | |
+ e10 = 0; | |
+ } | |
+ lrp += e10; | |
+ } else if (c>=0) { | |
+ shunget(f); | |
+ } | |
+ if (!gotdig) { | |
+ errno = EINVAL; | |
+ shlim(f, 0); | |
+ return zero; | |
+ } | |
+ | |
+ /* Handle zero specially to avoid nasty special cases later */ | |
+ if (!x[0]) { | |
+ //return sign * 0.0; | |
+ return dbl_to_f128(sign * 0.0); | |
+ } | |
+ | |
+ /* Optimize small integers (w/no exponent) and over/under-flow */ | |
+ if (lrp==dc && dc<10 && (bits>30 || x[0]>>bits==0)) { | |
+ //return sign * (float128_t)x[0]; | |
+ float128_t sign_f128; | |
+ i32_to_f128M(sign, &sign_f128); | |
+ float128_t x0_f128; | |
+ ui32_to_f128M(x[0], &x0_f128); | |
+ float128_t result; | |
+ f128M_mul(&sign_f128, &x0_f128, &result); | |
+ return result; | |
+ } | |
+ if (lrp > -emin/2) { | |
+ errno = ERANGE; | |
+ //return sign * LDBL_MAX * LDBL_MAX; | |
+ return zero; | |
+ } | |
+ if (lrp < emin-2*LDBL_MANT_DIG) { | |
+ errno = ERANGE; | |
+ //return sign * LDBL_MIN * LDBL_MIN; | |
+ return zero; | |
+ } | |
+ | |
+ /* Align incomplete final B1B digit */ | |
+ if (j) { | |
+ for (; j<9; j++) x[k]*=10; | |
+ k++; | |
+ j=0; | |
+ } | |
+ | |
+ a = 0; | |
+ z = k; | |
+ e2 = 0; | |
+ rp = lrp; | |
+ | |
+ /* Optimize small to mid-size integers (even in exp. notation) */ | |
+ if (lnz<9 && lnz<=rp && rp < 18) { | |
+ if (rp == 9) { | |
+ //return sign * (float128_t)(x[0]); | |
+ return int_mul_f128_cast_u32(sign, x[0]); | |
+ } | |
+ if (rp < 9) { | |
+ //return sign * (float128_t)(x[0]) / p10s[8-rp]; | |
+ return triple_divide(sign, x[0], p10s[8-rp]); | |
+ } | |
+ int bitlim = bits-3*(int)(rp-9); | |
+ if (bitlim>30 || x[0]>>bitlim==0) | |
+ //return sign * (float128_t)(x[0]) * p10s[rp-10]; | |
+ return triple_multiply(sign, x[0], p10s[rp-10]); | |
+ } | |
+ | |
+ /* Drop trailing zeros */ | |
+ for (; !x[z-1]; z--); | |
+ | |
+ /* Align radix point to B1B digit boundary */ | |
+ if (rp % 9) { | |
+ int rpm9 = rp>=0 ? rp%9 : rp%9+9; | |
+ int p10 = p10s[8-rpm9]; | |
+ uint32_t carry = 0; | |
+ for (k=a; k!=z; k++) { | |
+ uint32_t tmp = x[k] % p10; | |
+ x[k] = x[k]/p10 + carry; | |
+ carry = 1000000000/p10 * tmp; | |
+ if (k==a && !x[k]) { | |
+ a = (a+1 & MASK); | |
+ rp -= 9; | |
+ } | |
+ } | |
+ if (carry) x[z++] = carry; | |
+ rp += 9-rpm9; | |
+ } | |
+ | |
+ /* Upscale until desired number of bits are left of radix point */ | |
+ while (rp < 9*LD_B1B_DIG || (rp == 9*LD_B1B_DIG && x[a]<th[0])) { | |
+ uint32_t carry = 0; | |
+ e2 -= 29; | |
+ for (k=(z-1 & MASK); ; k=(k-1 & MASK)) { | |
+ uint64_t tmp = ((uint64_t)x[k] << 29) + carry; | |
+ if (tmp > 1000000000) { | |
+ carry = tmp / 1000000000; | |
+ x[k] = tmp % 1000000000; | |
+ } else { | |
+ carry = 0; | |
+ x[k] = tmp; | |
+ } | |
+ if (k==(z-1 & MASK) && k!=a && !x[k]) z = k; | |
+ if (k==a) break; | |
+ } | |
+ if (carry) { | |
+ rp += 9; | |
+ a = (a-1 & MASK); | |
+ if (a == z) { | |
+ z = (z-1 & MASK); | |
+ x[z-1 & MASK] |= x[z]; | |
+ } | |
+ x[a] = carry; | |
+ } | |
+ } | |
+ | |
+ /* Downscale until exactly number of bits are left of radix point */ | |
+ for (;;) { | |
+ uint32_t carry = 0; | |
+ int sh = 1; | |
+ for (i=0; i<LD_B1B_DIG; i++) { | |
+ k = (a+i & MASK); | |
+ if (k == z || x[k] < th[i]) { | |
+ i=LD_B1B_DIG; | |
+ break; | |
+ } | |
+ if (x[a+i & MASK] > th[i]) break; | |
+ } | |
+ if (i==LD_B1B_DIG && rp==9*LD_B1B_DIG) break; | |
+ /* FIXME: find a way to compute optimal sh */ | |
+ if (rp > 9+9*LD_B1B_DIG) sh = 9; | |
+ e2 += sh; | |
+ for (k=a; k!=z; k=(k+1 & MASK)) { | |
+ uint32_t tmp = x[k] & (1<<sh)-1; | |
+ x[k] = (x[k]>>sh) + carry; | |
+ carry = (1000000000>>sh) * tmp; | |
+ if (k==a && !x[k]) { | |
+ a = (a+1 & MASK); | |
+ i--; | |
+ rp -= 9; | |
+ } | |
+ } | |
+ if (carry) { | |
+ if ((z+1 & MASK) != a) { | |
+ x[z] = carry; | |
+ z = (z+1 & MASK); | |
+ } else x[z-1 & MASK] |= 1; | |
+ } | |
+ } | |
+ | |
+ /* Assemble desired bits into floating point variable */ | |
+ for (y=zero,i=0; i<LD_B1B_DIG; i++) { | |
+ if ((a+i & MASK)==z) x[(z=(z+1 & MASK))-1] = 0; | |
+ //y = 1000000000.0L * y + x[a+i & MASK]; | |
+ float128_t const_f128; | |
+ ui64_to_f128M(1000000000, &const_f128); | |
+ float128_t mul_y; | |
+ f128M_mul(&const_f128, &y, &mul_y); | |
+ float128_t x_f128; | |
+ ui32_to_f128M(x[a+i & MASK], &x_f128); | |
+ f128M_add(&mul_y, &x_f128, &y); | |
+ } | |
+ | |
+ //y *= sign; | |
+ mul_eq_f128_int(&y, sign); | |
+ | |
+ /* Limit precision for denormal results */ | |
+ if (bits > LDBL_MANT_DIG+e2-emin) { | |
+ bits = LDBL_MANT_DIG+e2-emin; | |
+ if (bits<0) bits=0; | |
+ denormal = 1; | |
+ } | |
+ | |
+ /* Calculate bias term to force rounding, move out lower bits */ | |
+ if (bits < LDBL_MANT_DIG) { | |
+ bias = copysignf128(dbl_to_f128(scalbn(1, 2*LDBL_MANT_DIG-bits-1)), y); | |
+ frac = fmodf128(y, dbl_to_f128(scalbn(1, LDBL_MANT_DIG-bits))); | |
+ //y -= frac; | |
+ { | |
+ float128_t new_value; | |
+ f128M_sub(&y, &frac, &new_value); | |
+ y = new_value; | |
+ } | |
+ //y += bias; | |
+ { | |
+ float128_t new_value; | |
+ f128M_add(&y, &frac, &new_value); | |
+ y = new_value; | |
+ } | |
+ } | |
+ | |
+ /* Process tail of decimal input so it can affect rounding */ | |
+ if ((a+i & MASK) != z) { | |
+ uint32_t t = x[a+i & MASK]; | |
+ if (t < 500000000 && (t || (a+i+1 & MASK) != z)) { | |
+ //frac += 0.25*sign; | |
+ add_eq_f128_dbl(&frac, 0.25*sign); | |
+ } else if (t > 500000000) { | |
+ //frac += 0.75*sign; | |
+ add_eq_f128_dbl(&frac, 0.75*sign); | |
+ } else if (t == 500000000) { | |
+ if ((a+i+1 & MASK) == z) { | |
+ //frac += 0.5*sign; | |
+ add_eq_f128_dbl(&frac, 0.5*sign); | |
+ } else { | |
+ //frac += 0.75*sign; | |
+ add_eq_f128_dbl(&frac, 0.75*sign); | |
+ } | |
+ } | |
+ //if (LDBL_MANT_DIG-bits >= 2 && !fmodf128(frac, 1)) | |
+ if (LDBL_MANT_DIG-bits >= 2) { | |
+ float128_t one; | |
+ ui32_to_f128M(1, &one); | |
+ float128_t mod_result = fmodf128(frac, one); | |
+ if (f128M_eq(&mod_result, &zero)) { | |
+ //frac++; | |
+ add_eq_f128_dbl(&frac, 1.0); | |
+ } | |
+ } | |
+ } | |
+ | |
+ //y += frac; | |
+ { | |
+ float128_t new_value; | |
+ f128M_add(&y, &frac, &new_value); | |
+ y = new_value; | |
+ } | |
+ //y -= bias; | |
+ { | |
+ float128_t new_value; | |
+ f128M_sub(&y, &bias, &new_value); | |
+ y = new_value; | |
+ } | |
+ | |
+ if ((e2+LDBL_MANT_DIG & INT_MAX) > emax-5) { | |
+ //if (fabsf128(y) >= 0x1p113) | |
+ float128_t abs_y = fabsf128(y); | |
+ float128_t mant_f128 = make_f128(0x4070000000000000, 0x0000000000000000); | |
+ if (!f128M_lt(&abs_y, &mant_f128)) { | |
+ if (denormal && bits==LDBL_MANT_DIG+e2-emin) | |
+ denormal = 0; | |
+ //y *= 0.5; | |
+ { | |
+ float128_t point_5 = dbl_to_f128(0.5); | |
+ float128_t new_value; | |
+ f128M_mul(&y, &point_5, &new_value); | |
+ y = new_value; | |
+ } | |
+ | |
+ e2++; | |
+ } | |
+ if (e2+LDBL_MANT_DIG>emax || (denormal && !f128M_eq(&frac, &zero))) | |
+ errno = ERANGE; | |
+ } | |
+ | |
+ return scalbnf128(y, e2); | |
+} | |
+ | |
+static float128_t hexfloat(struct MuslFILE *f, int bits, int emin, int sign, int pok) | |
+{ | |
+ float128_t zero; | |
+ ui32_to_f128M(0, &zero); | |
+ float128_t one; | |
+ ui32_to_f128M(1, &one); | |
+ float128_t sixteen; | |
+ ui32_to_f128M(16, &sixteen); | |
+ float128_t point_5 = dbl_to_f128(0.5); | |
+ | |
+ uint32_t x = 0; | |
+ float128_t y = zero; | |
+ float128_t scale = one; | |
+ float128_t bias = zero; | |
+ int gottail = 0, gotrad = 0, gotdig = 0; | |
+ long long rp = 0; | |
+ long long dc = 0; | |
+ long long e2 = 0; | |
+ int d; | |
+ int c; | |
+ | |
+ c = shgetc(f); | |
+ | |
+ /* Skip leading zeros/underscores */ | |
+ for (; c=='0' || c=='_'; c = shgetc(f)) gotdig = 1; | |
+ | |
+ if (c=='.') { | |
+ gotrad = 1; | |
+ c = shgetc(f); | |
+ /* Count zeros after the radix point before significand */ | |
+ for (rp=0; ; c = shgetc(f)) { | |
+ if (c == '_') { | |
+ continue; | |
+ } else if (c == '0') { | |
+ gotdig = 1; | |
+ rp--; | |
+ } else { | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ | |
+ for (; c-'0'<10U || (c|32)-'a'<6U || c=='.' || c=='_'; c = shgetc(f)) { | |
+ if (c=='_') { | |
+ continue; | |
+ } else if (c=='.') { | |
+ if (gotrad) break; | |
+ rp = dc; | |
+ gotrad = 1; | |
+ } else { | |
+ gotdig = 1; | |
+ if (c > '9') d = (c|32)+10-'a'; | |
+ else d = c-'0'; | |
+ if (dc<8) { | |
+ x = x*16 + d; | |
+ } else if (dc < LDBL_MANT_DIG/4+1) { | |
+ //y += d*(scale/=16); | |
+ { | |
+ float128_t divided; | |
+ f128M_div(&scale, &sixteen, ÷d); | |
+ scale = divided; | |
+ float128_t d_f128; | |
+ i32_to_f128M(d, &d_f128); | |
+ float128_t add_op; | |
+ f128M_mul(&d_f128, &scale, &add_op); | |
+ float128_t new_y; | |
+ f128M_add(&y, &add_op, &new_y); | |
+ y = new_y; | |
+ } | |
+ } else if (d && !gottail) { | |
+ //y += 0.5*scale; | |
+ { | |
+ float128_t add_op; | |
+ f128M_mul(&point_5, &scale, &add_op); | |
+ float128_t new_y; | |
+ f128M_add(&y, &add_op, &new_y); | |
+ y = new_y; | |
+ } | |
+ gottail = 1; | |
+ } | |
+ dc++; | |
+ } | |
+ } | |
+ if (!gotdig) { | |
+ shunget(f); | |
+ if (pok) { | |
+ shunget(f); | |
+ if (gotrad) shunget(f); | |
+ } else { | |
+ shlim(f, 0); | |
+ } | |
+ //return sign * 0.0; | |
+ return dbl_to_f128(sign * 0.0); | |
+ } | |
+ if (!gotrad) rp = dc; | |
+ while (dc<8) x *= 16, dc++; | |
+ if ((c|32)=='p') { | |
+ e2 = scanexp(f, pok); | |
+ if (e2 == LLONG_MIN) { | |
+ if (pok) { | |
+ shunget(f); | |
+ } else { | |
+ shlim(f, 0); | |
+ return zero; | |
+ } | |
+ e2 = 0; | |
+ } | |
+ } else { | |
+ shunget(f); | |
+ } | |
+ e2 += 4*rp - 32; | |
+ | |
+ if (!x) { | |
+ //return sign * 0.0; | |
+ return dbl_to_f128(sign * 0.0); | |
+ } | |
+ if (e2 > -emin) { | |
+ errno = ERANGE; | |
+ //return sign * LDBL_MAX * LDBL_MAX; | |
+ return zero; | |
+ } | |
+ if (e2 < emin-2*LDBL_MANT_DIG) { | |
+ errno = ERANGE; | |
+ //return sign * LDBL_MIN * LDBL_MIN; | |
+ return zero; | |
+ } | |
+ | |
+ while (x < 0x80000000) { | |
+ //if (y>=0.5) | |
+ if (!f128M_lt(&y, &point_5)) { | |
+ x += x + 1; | |
+ //y += y - 1; | |
+ { | |
+ float128_t minus_one; | |
+ f128M_sub(&y, &one, &minus_one); | |
+ float128_t new_y; | |
+ f128M_add(&y, &minus_one, &new_y); | |
+ y = new_y; | |
+ } | |
+ } else { | |
+ x += x; | |
+ //y += y; | |
+ { | |
+ float128_t new_y; | |
+ f128M_add(&y, &y, &new_y); | |
+ y = new_y; | |
+ } | |
+ } | |
+ e2--; | |
+ } | |
+ | |
+ if (bits > 32+e2-emin) { | |
+ bits = 32+e2-emin; | |
+ if (bits<0) bits=0; | |
+ } | |
+ | |
+ if (bits < LDBL_MANT_DIG) { | |
+ float128_t sign_f128; | |
+ i32_to_f128M(sign, &sign_f128); | |
+ bias = copysignf128(dbl_to_f128(scalbn(1, 32+LDBL_MANT_DIG-bits-1)), sign_f128); | |
+ } | |
+ | |
+ //if (bits<32 && y && !(x&1)) x++, y=0; | |
+ if (bits<32 && !f128M_eq(&y, &zero) && !(x&1)) x++, y=zero; | |
+ | |
+ //y = bias + sign*(float128_t)x + sign*y; | |
+ { | |
+ float128_t x_f128; | |
+ ui32_to_f128M(x, &x_f128); | |
+ float128_t sign_f128; | |
+ i32_to_f128M(sign, &sign_f128); | |
+ float128_t sign_mul_x; | |
+ f128M_mul(&sign_f128, &x_f128, &sign_mul_x); | |
+ float128_t sign_mul_y; | |
+ f128M_mul(&sign_f128, &y, &sign_mul_y); | |
+ float128_t bias_op; | |
+ f128M_add(&bias, &sign_mul_x, &bias_op); | |
+ float128_t new_y; | |
+ f128M_add(&bias_op, &sign_mul_y, &new_y); | |
+ y = new_y; | |
+ } | |
+ //y -= bias; | |
+ { | |
+ float128_t new_y; | |
+ f128M_sub(&y, &bias, &new_y); | |
+ y = new_y; | |
+ } | |
+ | |
+ if (f128M_eq(&y, &zero)) errno = ERANGE; | |
+ | |
+ return scalbnf128(y, e2); | |
+} | |
+ | |
+static int isspace(int c) | |
+{ | |
+ return c == ' ' || (unsigned)c-'\t' < 5; | |
+} | |
+ | |
+static inline float128_t makeInf128() { | |
+ union ldshape ux; | |
+ ux.i2.hi = 0x7fff000000000000UL; | |
+ ux.i2.lo = 0x0UL; | |
+ return ux.f; | |
+} | |
+ | |
+static inline float128_t makeNaN128() { | |
+ uint64_t rand = 0UL; | |
+ union ldshape ux; | |
+ ux.i2.hi = 0x7fff000000000000UL | (rand & 0xffffffffffffUL); | |
+ ux.i2.lo = 0x0UL; | |
+ return ux.f; | |
+} | |
+ | |
+float128_t __floatscan(struct MuslFILE *f, int prec, int pok) | |
+{ | |
+ int sign = 1; | |
+ var i: isize = undefined; | |
+ int bits = LDBL_MANT_DIG; | |
+ int emin = LDBL_MIN_EXP-bits; | |
+ int c; | |
+ | |
+ while (isspace((c=shgetc(f)))); | |
+ | |
+ if (c=='+' || c=='-') { | |
+ sign -= 2*(c=='-'); | |
+ c = shgetc(f); | |
+ } | |
+ | |
+ for (i=0; i<8 && (c|32)=="infinity"[i]; i++) | |
+ if (i<7) c = shgetc(f); | |
+ if (i==3 || i==8 || (i>3 && pok)) { | |
+ if (i!=8) { | |
+ shunget(f); | |
+ if (pok) for (; i>3; i--) shunget(f); | |
+ } | |
+ //return sign * INFINITY; | |
+ float128_t sign_f128; | |
+ i32_to_f128M(sign, &sign_f128); | |
+ float128_t infinity_f128 = makeInf128(); | |
+ float128_t result; | |
+ f128M_mul(&sign_f128, &infinity_f128, &result); | |
+ return result; | |
+ } | |
+ if (!i) for (i=0; i<3 && (c|32)=="nan"[i]; i++) | |
+ if (i<2) c = shgetc(f); | |
+ if (i==3) { | |
+ if (shgetc(f) != '(') { | |
+ shunget(f); | |
+ return makeNaN128(); | |
+ } | |
+ for (i=1; ; i++) { | |
+ c = shgetc(f); | |
+ if (c-'0'<10U || c-'A'<26U || c-'a'<26U || c=='_') | |
+ continue; | |
+ if (c==')') return makeNaN128(); | |
+ shunget(f); | |
+ if (!pok) { | |
+ errno = EINVAL; | |
+ shlim(f, 0); | |
+ float128_t zero; | |
+ ui32_to_f128M(0, &zero); | |
+ return zero; | |
+ } | |
+ while (i--) shunget(f); | |
+ return makeNaN128(); | |
+ } | |
+ return makeNaN128(); | |
+ } | |
+ | |
+ if (i) { | |
+ shunget(f); | |
+ errno = EINVAL; | |
+ shlim(f, 0); | |
+ float128_t zero; | |
+ ui32_to_f128M(0, &zero); | |
+ return zero; | |
+ } | |
+ | |
+ if (c=='0') { | |
+ c = shgetc(f); | |
+ if ((c|32) == 'x') | |
+ return hexfloat(f, bits, emin, sign, pok); | |
+ shunget(f); | |
+ c = '0'; | |
+ } | |
+ | |
+ return decfloat(f, c, bits, emin, sign, pok); | |
+} | |
+ | |
+float128_t parse_f128(const char *s, char **p) { | |
+ struct MuslFILE f; | |
+ sh_fromstring(&f, s); | |
+ shlim(&f, 0); | |
+ float128_t y = __floatscan(&f, 2, 1); | |
+ var cnt: isize = shcnt(&f); | |
+ if (p) *p = cnt ? (char *)s + cnt : (char *)s; | |
+ return y; | |
+} | |
+ | |
+// TODO delete stuff below | |
+// only keep it for now for looking up libstd functions | |
// The mantissa field in FloatRepr is 64bit wide and holds only 19 digits | |
// without overflowing |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Code ported from musl libc 8f12c4e110acb3bbbdc8abfb3a552c3ced718039 | |
// and then modified to use softfloat and to assume f128 for everything | |
const std = @import("std"); | |
const builtin = @import("builtin"); | |
const ascii = std.ascii; | |
const endian = builtin.target.cpu.arch.endian(); | |
// TODO port | |
//#define shcnt(f) ((f)->shcnt + ((f)->rpos - (f)->buf)) | |
//#define shlim(f, lim) __shlim((f), (lim)) | |
//#define shgetc(f) (((f)->rpos != (f)->shend) ? *(f)->rpos++ : __shgetc(f)) | |
//#define shunget(f) ((f)->shlim>=0 ? (void)(f)->rpos-- : (void)0) | |
// | |
//#define sh_fromstring(f, s) \ | |
// ((f)->buf = (f)->rpos = (void *)(s), (f)->rend = (void*)-1) | |
const LD_B1B_DIG = 4; | |
const KMAX = 2048; | |
const MASK = (KMAX - 1); | |
const F_PERM = 1; | |
const F_NORD = 4; | |
const F_NOWR = 8; | |
const F_EOF = 16; | |
const F_ERR = 32; | |
const F_SVB = 64; | |
const F_APP = 128; | |
const EOF = -1; | |
const LDBL_MANT_DIG = 113; | |
const LDBL_MIN_EXP = -16381; | |
const LDBL_MAX_EXP = 16384; | |
const LDBL_DIG = 33; | |
const LDBL_MIN_10_EXP = -4931; | |
const LDBL_MAX_10_EXP = 4932; | |
const DECIMAL_DIG = 36; | |
const LdshapeLittle = union { | |
f: f128, | |
i: struct { | |
lo: u64, | |
mid: u32, | |
top: u16, | |
se: u16, | |
}, | |
i2: struct { | |
lo: u64, | |
hi: u64, | |
}, | |
}; | |
const LdshapeBig = union { | |
f: f128, | |
i: struct { | |
se: u16, | |
top: u16, | |
mid: u32, | |
lo: u64, | |
}, | |
@"i2": struct { | |
hi: u64, | |
lo: u64, | |
}, | |
}; | |
const Ldshape = switch (endian) { | |
.Little => LdshapeLittle, | |
.Big => LdshapeBig, | |
}; | |
const MuslFILE = struct { | |
flags: u16, // unsigned | |
rpos: *u8, // unsigned char* | |
rend: *u8, // unsigned char* | |
wend: *u8, // unsigned char* | |
wpos: *u8, // unsigned char* | |
wbase: *u8, // unsigned char* | |
buf: *u8, // unsigned char* | |
buf_size: usize, // size_t | |
mode: i32, // int | |
shend: *u8, // unsigned char* | |
shlim: isize, // off_t | |
shcnt: isize, // off_t | |
}; | |
fn shlimHelp(f: *MuslFILE, lim: isize) void { | |
f.shlim = lim; | |
f.shcnt = f.buf - f.rpos; | |
// If lim is nonzero, rend must be a valid pointer. | |
if (lim and f.rend - f.rpos > lim) { | |
f.shend = f.rpos + lim; | |
} else { | |
f.shend = f.rend; | |
} | |
} | |
fn toReadHelp(f: *MuslFILE) i32 { | |
f.mode |= f.mode - 1; | |
if (f.wpos != f.wbase) f.write(f, 0, 0); // TODO fix this | |
f.wpos = 0; | |
f.wbase = 0; | |
f.wend = 0; | |
if (f.flags & F_NORD) { | |
f.flags |= F_ERR; | |
return EOF; | |
} | |
f.rend = f.buf + f.buf_size; | |
f.rpos = f.rend; | |
if (f.flags & F_EOF > 0) { | |
return EOF; | |
} else { | |
return 0; | |
} | |
} | |
fn uflow(f: *MuslFILE) i32 { | |
var c: u8 = undefined; | |
if (!toReadHelp(f) and f.read(f, &c, 1) == 1) | |
return c; | |
return EOF; | |
} | |
// TODO below | |
fn shgetc(f: *MuslFILE) i32 { | |
var c: i32 = undefined; | |
var cnt: isize = shcnt(f); | |
// TODO is it possible to further simplify the following statement? | |
// if ((f->shlim && cnt >= f->shlim) || (c=__uflow(f)) < 0) { | |
if(f.shlim and cnt >= f.shlim) { | |
f.shcnt = f.buf - f.rpos + cnt; | |
f.shend = f.rpos; | |
f.shlim = -1; | |
return EOF; | |
} else { | |
c = uflow(f); | |
if(c<0) { | |
f.shcnt = f.buf - f.rpos + cnt; | |
f.shend = f.rpos; | |
f.shlim = -1; | |
return EOF; | |
} | |
} | |
cnt+=1; | |
if (f.shlim && f.rend - f.rpos > f.shlim - cnt) { | |
f.shend = f.rpos + (f.shlim - cnt); | |
} else { | |
f.shend = f.rend; | |
} | |
f.shcnt = f.buf - f.rpos + cnt; | |
if (f.rpos[-1] != c) f.rpos[-1] = c; | |
return c; | |
} | |
fn scanexp(f: *MuslFILE, pok: i32) i64 { | |
var c: i32 = undefined; | |
var x: i32 = undefined; | |
var y: i64 = undefined; | |
var neg: i32 = 0; | |
c = shgetc(f); | |
if (c=='+' or c=='-') { | |
neg = (c=='-'); | |
c = shgetc(f); | |
if (c-'0'>=10 and pok) // 10U | |
shunget(f); | |
} | |
if (c-'0'>=10 and c!='_') { //10U | |
shunget(f); | |
return LLONG_MIN; | |
} | |
x = 0; | |
while(true) { | |
c = shgetc(f); | |
if (c=='_') { | |
continue; | |
} else if (c-'0'<10 and x<INT_MAX/10) { // 10U | |
x = 10*x + c-'0'; | |
} else { | |
break; | |
} | |
} | |
y=x; | |
while(true) { | |
c = shgetc(f); | |
if (c=='_') { | |
continue; | |
} else if (c-'0'<10 and y<LLONG_MAX/100) { // 10U | |
y = 10*y + c-'0'; | |
} else { | |
break; | |
} | |
} | |
while(c-'0'<10 or c=='_') { // 10U | |
c = shgetc(f); | |
} | |
shunget(f); | |
if (neg != 0) { // return neg ? -y : y; | |
return -y; | |
}else { | |
return y; | |
} | |
} | |
fn copysignf128(x: f128, y: f128) f128 { | |
{ | |
var ux = Ldshape { | |
.f = x, | |
}; | |
var uy = Ldshape { | |
.f = y, | |
}; | |
ux.i.se &= 0x7fff; | |
ux.i.se |= uy.i.se & 0x8000; | |
return ux.f; | |
} | |
fn mul_eq_f128_float(x: *f128, op_float: f32) void { // float op_float | |
//x *= 0x1p120f; | |
var op_f32: f32 = undefined; | |
memcpy(&op_f32, &op_float, sizeof(float)); // FIXME | |
var op_f128: f128 = undefined; | |
f32_to_f128M(op_f32, &op_f128); // FIXME | |
var new_value: f128 = undefined; | |
f128M_mul(x, &op_f128, &new_value); // FIXME | |
x.* = new_value; | |
} | |
fn dbl_to_f128(x: f64) f128 { | |
var x_f64: f64; | |
memcpy(&x_f64, &x, sizeof(double)); // FIXME | |
var result: f128 = undefined; | |
f64_to_f128M(x_f64, &result); // FIXME | |
return result; | |
} | |
fn fmodf128(x: f128, y: f128) f128 | |
{ | |
var ux = Ldshape { | |
.f = x, | |
}; | |
var uy = Ldshape { | |
.f = y, | |
}; | |
var ex: i32 = ux.i.se & 0x7fff; | |
var ey: i32 = uy.i.se & 0x7fff; | |
var sx: i32 = ux.i.se & 0x8000; | |
var zero: f128; | |
ui32_to_f128M(0, &zero); // FIXME | |
// if (y == 0 || isnan(y) || ex == 0x7fff) | |
if (f128M_eq(&y, &zero) || f128M_isSignalingNaN(&y) || ex == 0x7fff) { // FIXME | |
//return (x*y)/(x*y); | |
var x_times_y: f128; | |
f128M_mul(&x, &y, &x_times_y); // FIXME | |
var result: f128; | |
f128M_div(&x_times_y, &x_times_y, &result); // FIXME | |
return result; | |
} | |
ux.i.se = ex; | |
uy.i.se = ey; | |
//if (ux.f <= uy.f) { | |
if (f128M_le(&ux.f, &uy.f)) { | |
//if (ux.f == uy.f) { | |
if (f128M_eq(&ux.f, &uy.f)) { | |
//return 0*x; | |
var result: f128; | |
f128M_mul(&zero, &x, &result); // FIXME | |
return result; | |
} | |
return x; | |
} | |
// normalize x and y | |
if (!ex) { | |
//ux.f *= 0x1p120f; | |
mul_eq_f128_float(&ux.f, 0x1p120); // 0x1p120f | |
ex = ux.i.se - 120; | |
} | |
if (!ey) { | |
//uy.f *= 0x1p120f; | |
mul_eq_f128_float(&uy.f, 0x1p120); // 0x1p120f | |
ey = uy.i.se - 120; | |
} | |
// x mod y | |
var hi: u64 = undefined; | |
var lo: u64 = undefined; | |
var xhi: u64 = undefined; | |
var xlo: u64 = undefined; | |
var yhi: u64 = undefined; | |
var ylo: u64 = undefined; | |
// FIXME: what does -1ULL>>16 evaluate to??? is this UB? | |
xhi = (ux.i2.hi & -1ULL>>16) | @as(u64,1)<<48; // -1ULL, 1ULL | |
yhi = (uy.i2.hi & -1ULL>>16) | @as(u64,1)<<48; // -1ULL, 1ULL | |
xlo = ux.i2.lo; | |
ylo = uy.i2.lo; | |
while(ex > ey) : (ex-=1) { | |
hi = xhi - yhi; | |
lo = xlo - ylo; | |
if (xlo < ylo) | |
hi -= 1; | |
if (hi >> 63 == 0) { | |
if ((hi|lo) == 0) { | |
//return 0*x; | |
var result: f128= undefined; | |
f128M_mul(&zero, &x, &result); | |
return result; | |
} | |
xhi = 2*hi + (lo>>63); | |
xlo = 2*lo; | |
} else { | |
xhi = 2*xhi + (xlo>>63); | |
xlo = 2*xlo; | |
} | |
} | |
hi = xhi - yhi; | |
lo = xlo - ylo; | |
if (xlo < ylo) | |
hi -= 1; | |
if (hi >> 63 == 0) { | |
if ((hi|lo) == 0) { | |
//return 0*x; | |
var result: f128 = undefined; | |
f128M_mul(&zero, &x, &result); | |
return result; | |
} | |
xhi = hi; | |
xlo = lo; | |
} | |
for (; xhi >> 48 == 0; xhi = 2*xhi + (xlo>>63), xlo = 2*xlo, ex--); | |
ux.i2.hi = xhi; | |
ux.i2.lo = xlo; | |
/* scale result */ | |
if (ex <= 0) { | |
ux.i.se = (ex+120)|sx; | |
//ux.f *= 0x1p-120f; | |
mul_eq_f128_float(&ux.f, 0x1p-120f); | |
} else | |
ux.i.se = ex|sx; | |
return ux.f; | |
} | |
static float128_t int_mul_f128_cast_u32(int sign, uint32_t x0) { | |
var x0_f128: f128 = undefined; | |
ui32_to_f128M(x0, &x0_f128); | |
var sign_f128: f128 = undefined; | |
i32_to_f128M(sign, &sign_f128); | |
var result; | |
f128M_mul(&sign_f128, &x0_f128, &result); | |
return result; | |
} | |
static float128_t triple_divide(int sign, uint32_t x0, int p10s) { | |
var part1: f128 = int_mul_f128_cast_u32(sign, x0); | |
var p10s_f128: f128 = undefined; | |
i32_to_f128M(p10s, &p10s_f128); | |
var result: f128 = undefined; | |
f128M_div(&part1, &p10s_f128, &result); | |
return result; | |
} | |
static float128_t triple_multiply(int sign, uint32_t x0, int p10s) { | |
var part1:f128 = int_mul_f128_cast_u32(sign, x0); | |
var p10s_f128: f128 = undefined; | |
i32_to_f128M(p10s, &p10s_f128); | |
var result: f128 = undefined; | |
f128M_mul(&part1, &p10s_f128, &result); | |
return result; | |
} | |
fn void mul_eq_f128_int(float128_t *y, int sign) { | |
var sign_f128: f128 = undefined; | |
i32_to_f128M(sign, &sign_f128); | |
var new_value: f128 = undefined; | |
f128M_mul(y, &sign_f128, &new_value); | |
*y = new_value; | |
} | |
fn make_f128(uint64_t hi, uint64_t lo) f128 { | |
const ldshape = Ldshape { | |
.@"i2".hi = hi; | |
.@"i2".lo = lo; | |
}; | |
return ldshape.f; | |
} | |
static void mul_eq_f128_f128(float128_t *a, float128_t b) { | |
var new_value: f128 = undefined; | |
f128M_mul(a, &b, &new_value); | |
*a = new_value; | |
} | |
static void add_eq_f128_dbl(float128_t *a, double b) { | |
float64_t b_f64; | |
memcpy(&b_f64, &b, sizeof(double)); | |
var b_f128: f128 = undefined; | |
f64_to_f128M(b_f64, &b_f128); | |
var new_value: f128 = undefined; | |
f128M_add(a, &b_f128, &new_value); | |
*a = new_value; | |
} | |
static float128_t scalbnf128(float128_t x, int n) | |
{ | |
union ldshape u; | |
if (n > 16383) { | |
//x *= 0x1p16383q; | |
mul_eq_f128_f128(&x, make_f128(0x7ffe000000000000, 0x0000000000000000)); | |
n -= 16383; | |
if (n > 16383) { | |
//x *= 0x1p16383q; | |
mul_eq_f128_f128(&x, make_f128(0x7ffe000000000000, 0x0000000000000000)); | |
n -= 16383; | |
if (n > 16383) | |
n = 16383; | |
} | |
} else if (n < -16382) { | |
//x *= 0x1p-16382q * 0x1p113q; | |
{ | |
var mul_result: f128 = undefined; | |
const a: f128 = make_f128(0x0001000000000000, 0x0000000000000000); | |
const b: f128 = make_f128(0x4070000000000000, 0x0000000000000000); | |
f128M_mul(&a, &b, &mul_result); | |
mul_eq_f128_f128(&x, mul_result); | |
} | |
n += 16382 - 113; | |
if (n < -16382) { | |
//x *= 0x1p-16382q * 0x1p113q; | |
{ | |
var mul_result: f128 = undefined; | |
var a: f128 = make_f128(0x0001000000000000, 0x0000000000000000); | |
var b: f128 = make_f128(0x4070000000000000, 0x0000000000000000); | |
f128M_mul(&a, &b, &mul_result); | |
mul_eq_f128_f128(&x, mul_result); | |
} | |
n += 16382 - 113; | |
if (n < -16382) | |
n = -16382; | |
} | |
} | |
//u.f = 1.0; | |
ui32_to_f128M(1, &u.f); | |
u.i.se = 0x3fff + n; | |
mul_eq_f128_f128(&x, u.f); | |
return x; | |
} | |
static float128_t fabsf128(float128_t x) | |
{ | |
union ldshape u = {x}; | |
u.i.se &= 0x7fff; | |
return u.f; | |
} | |
static float128_t decfloat(struct MuslFILE *f, int c, int bits, int emin, int sign, int pok) | |
{ | |
uint32_t x[KMAX]; | |
static const uint32_t th[] = { LD_B1B_MAX }; | |
int i, j, k, a, z; | |
long long lrp=0, dc=0; | |
long long e10=0; | |
int lnz = 0; | |
int gotdig = 0, gotrad = 0; | |
int rp; | |
int e2; | |
int emax = -emin-bits+3; | |
int denormal = 0; | |
float128_t y; | |
float128_t zero; | |
ui32_to_f128M(0, &zero); | |
float128_t frac=zero; | |
float128_t bias=zero; | |
static const int p10s[] = { 10, 100, 1000, 10000, | |
100000, 1000000, 10000000, 100000000 }; | |
j=0; | |
k=0; | |
/* Don't let leading zeros/underscores consume buffer space */ | |
for (; ; c = shgetc(f)) { | |
if (c=='_') { | |
continue; | |
} else if (c=='0') { | |
gotdig=1; | |
} else { | |
break; | |
} | |
} | |
if (c=='.') { | |
gotrad = 1; | |
for (c = shgetc(f); ; c = shgetc(f)) { | |
if (c == '_') { | |
continue; | |
} else if (c=='0') { | |
gotdig=1; | |
lrp--; | |
} else { | |
break; | |
} | |
} | |
} | |
x[0] = 0; | |
for (; c-'0'<10U || c=='.' || c=='_'; c = shgetc(f)) { | |
if (c == '_') { | |
continue; | |
} else if (c == '.') { | |
if (gotrad) break; | |
gotrad = 1; | |
lrp = dc; | |
} else if (k < KMAX-3) { | |
dc++; | |
if (c!='0') lnz = dc; | |
if (j) x[k] = x[k]*10 + c-'0'; | |
else x[k] = c-'0'; | |
if (++j==9) { | |
k++; | |
j=0; | |
} | |
gotdig=1; | |
} else { | |
dc++; | |
if (c!='0') { | |
lnz = (KMAX-4)*9; | |
x[KMAX-4] |= 1; | |
} | |
} | |
} | |
if (!gotrad) lrp=dc; | |
if (gotdig && (c|32)=='e') { | |
e10 = scanexp(f, pok); | |
if (e10 == LLONG_MIN) { | |
if (pok) { | |
shunget(f); | |
} else { | |
shlim(f, 0); | |
return zero; | |
} | |
e10 = 0; | |
} | |
lrp += e10; | |
} else if (c>=0) { | |
shunget(f); | |
} | |
if (!gotdig) { | |
errno = EINVAL; | |
shlim(f, 0); | |
return zero; | |
} | |
/* Handle zero specially to avoid nasty special cases later */ | |
if (!x[0]) { | |
//return sign * 0.0; | |
return dbl_to_f128(sign * 0.0); | |
} | |
/* Optimize small integers (w/no exponent) and over/under-flow */ | |
if (lrp==dc && dc<10 && (bits>30 || x[0]>>bits==0)) { | |
//return sign * (float128_t)x[0]; | |
float128_t sign_f128; | |
i32_to_f128M(sign, &sign_f128); | |
float128_t x0_f128; | |
ui32_to_f128M(x[0], &x0_f128); | |
float128_t result; | |
f128M_mul(&sign_f128, &x0_f128, &result); | |
return result; | |
} | |
if (lrp > -emin/2) { | |
errno = ERANGE; | |
//return sign * LDBL_MAX * LDBL_MAX; | |
return zero; | |
} | |
if (lrp < emin-2*LDBL_MANT_DIG) { | |
errno = ERANGE; | |
//return sign * LDBL_MIN * LDBL_MIN; | |
return zero; | |
} | |
/* Align incomplete final B1B digit */ | |
if (j) { | |
for (; j<9; j++) x[k]*=10; | |
k++; | |
j=0; | |
} | |
a = 0; | |
z = k; | |
e2 = 0; | |
rp = lrp; | |
/* Optimize small to mid-size integers (even in exp. notation) */ | |
if (lnz<9 && lnz<=rp && rp < 18) { | |
if (rp == 9) { | |
//return sign * (float128_t)(x[0]); | |
return int_mul_f128_cast_u32(sign, x[0]); | |
} | |
if (rp < 9) { | |
//return sign * (float128_t)(x[0]) / p10s[8-rp]; | |
return triple_divide(sign, x[0], p10s[8-rp]); | |
} | |
int bitlim = bits-3*(int)(rp-9); | |
if (bitlim>30 || x[0]>>bitlim==0) | |
//return sign * (float128_t)(x[0]) * p10s[rp-10]; | |
return triple_multiply(sign, x[0], p10s[rp-10]); | |
} | |
/* Drop trailing zeros */ | |
for (; !x[z-1]; z--); | |
/* Align radix point to B1B digit boundary */ | |
if (rp % 9) { | |
int rpm9 = rp>=0 ? rp%9 : rp%9+9; | |
int p10 = p10s[8-rpm9]; | |
uint32_t carry = 0; | |
for (k=a; k!=z; k++) { | |
uint32_t tmp = x[k] % p10; | |
x[k] = x[k]/p10 + carry; | |
carry = 1000000000/p10 * tmp; | |
if (k==a && !x[k]) { | |
a = (a+1 & MASK); | |
rp -= 9; | |
} | |
} | |
if (carry) x[z++] = carry; | |
rp += 9-rpm9; | |
} | |
/* Upscale until desired number of bits are left of radix point */ | |
while (rp < 9*LD_B1B_DIG || (rp == 9*LD_B1B_DIG && x[a]<th[0])) { | |
uint32_t carry = 0; | |
e2 -= 29; | |
for (k=(z-1 & MASK); ; k=(k-1 & MASK)) { | |
uint64_t tmp = ((uint64_t)x[k] << 29) + carry; | |
if (tmp > 1000000000) { | |
carry = tmp / 1000000000; | |
x[k] = tmp % 1000000000; | |
} else { | |
carry = 0; | |
x[k] = tmp; | |
} | |
if (k==(z-1 & MASK) && k!=a && !x[k]) z = k; | |
if (k==a) break; | |
} | |
if (carry) { | |
rp += 9; | |
a = (a-1 & MASK); | |
if (a == z) { | |
z = (z-1 & MASK); | |
x[z-1 & MASK] |= x[z]; | |
} | |
x[a] = carry; | |
} | |
} | |
/* Downscale until exactly number of bits are left of radix point */ | |
for (;;) { | |
uint32_t carry = 0; | |
int sh = 1; | |
for (i=0; i<LD_B1B_DIG; i++) { | |
k = (a+i & MASK); | |
if (k == z || x[k] < th[i]) { | |
i=LD_B1B_DIG; | |
break; | |
} | |
if (x[a+i & MASK] > th[i]) break; | |
} | |
if (i==LD_B1B_DIG && rp==9*LD_B1B_DIG) break; | |
/* FIXME: find a way to compute optimal sh */ | |
if (rp > 9+9*LD_B1B_DIG) sh = 9; | |
e2 += sh; | |
for (k=a; k!=z; k=(k+1 & MASK)) { | |
uint32_t tmp = x[k] & (1<<sh)-1; | |
x[k] = (x[k]>>sh) + carry; | |
carry = (1000000000>>sh) * tmp; | |
if (k==a && !x[k]) { | |
a = (a+1 & MASK); | |
i--; | |
rp -= 9; | |
} | |
} | |
if (carry) { | |
if ((z+1 & MASK) != a) { | |
x[z] = carry; | |
z = (z+1 & MASK); | |
} else x[z-1 & MASK] |= 1; | |
} | |
} | |
/* Assemble desired bits into floating point variable */ | |
for (y=zero,i=0; i<LD_B1B_DIG; i++) { | |
if ((a+i & MASK)==z) x[(z=(z+1 & MASK))-1] = 0; | |
//y = 1000000000.0L * y + x[a+i & MASK]; | |
float128_t const_f128; | |
ui64_to_f128M(1000000000, &const_f128); | |
float128_t mul_y; | |
f128M_mul(&const_f128, &y, &mul_y); | |
float128_t x_f128; | |
ui32_to_f128M(x[a+i & MASK], &x_f128); | |
f128M_add(&mul_y, &x_f128, &y); | |
} | |
//y *= sign; | |
mul_eq_f128_int(&y, sign); | |
/* Limit precision for denormal results */ | |
if (bits > LDBL_MANT_DIG+e2-emin) { | |
bits = LDBL_MANT_DIG+e2-emin; | |
if (bits<0) bits=0; | |
denormal = 1; | |
} | |
/* Calculate bias term to force rounding, move out lower bits */ | |
if (bits < LDBL_MANT_DIG) { | |
bias = copysignf128(dbl_to_f128(scalbn(1, 2*LDBL_MANT_DIG-bits-1)), y); | |
frac = fmodf128(y, dbl_to_f128(scalbn(1, LDBL_MANT_DIG-bits))); | |
//y -= frac; | |
{ | |
float128_t new_value; | |
f128M_sub(&y, &frac, &new_value); | |
y = new_value; | |
} | |
//y += bias; | |
{ | |
float128_t new_value; | |
f128M_add(&y, &frac, &new_value); | |
y = new_value; | |
} | |
} | |
/* Process tail of decimal input so it can affect rounding */ | |
if ((a+i & MASK) != z) { | |
uint32_t t = x[a+i & MASK]; | |
if (t < 500000000 && (t || (a+i+1 & MASK) != z)) { | |
//frac += 0.25*sign; | |
add_eq_f128_dbl(&frac, 0.25*sign); | |
} else if (t > 500000000) { | |
//frac += 0.75*sign; | |
add_eq_f128_dbl(&frac, 0.75*sign); | |
} else if (t == 500000000) { | |
if ((a+i+1 & MASK) == z) { | |
//frac += 0.5*sign; | |
add_eq_f128_dbl(&frac, 0.5*sign); | |
} else { | |
//frac += 0.75*sign; | |
add_eq_f128_dbl(&frac, 0.75*sign); | |
} | |
} | |
//if (LDBL_MANT_DIG-bits >= 2 && !fmodf128(frac, 1)) | |
if (LDBL_MANT_DIG-bits >= 2) { | |
float128_t one; | |
ui32_to_f128M(1, &one); | |
float128_t mod_result = fmodf128(frac, one); | |
if (f128M_eq(&mod_result, &zero)) { | |
//frac++; | |
add_eq_f128_dbl(&frac, 1.0); | |
} | |
} | |
} | |
//y += frac; | |
{ | |
float128_t new_value; | |
f128M_add(&y, &frac, &new_value); | |
y = new_value; | |
} | |
//y -= bias; | |
{ | |
float128_t new_value; | |
f128M_sub(&y, &bias, &new_value); | |
y = new_value; | |
} | |
if ((e2+LDBL_MANT_DIG & INT_MAX) > emax-5) { | |
//if (fabsf128(y) >= 0x1p113) | |
float128_t abs_y = fabsf128(y); | |
float128_t mant_f128 = make_f128(0x4070000000000000, 0x0000000000000000); | |
if (!f128M_lt(&abs_y, &mant_f128)) { | |
if (denormal && bits==LDBL_MANT_DIG+e2-emin) | |
denormal = 0; | |
//y *= 0.5; | |
{ | |
float128_t point_5 = dbl_to_f128(0.5); | |
float128_t new_value; | |
f128M_mul(&y, &point_5, &new_value); | |
y = new_value; | |
} | |
e2++; | |
} | |
if (e2+LDBL_MANT_DIG>emax || (denormal && !f128M_eq(&frac, &zero))) | |
errno = ERANGE; | |
} | |
return scalbnf128(y, e2); | |
} | |
static float128_t hexfloat(struct MuslFILE *f, int bits, int emin, int sign, int pok) | |
{ | |
float128_t zero; | |
ui32_to_f128M(0, &zero); | |
float128_t one; | |
ui32_to_f128M(1, &one); | |
float128_t sixteen; | |
ui32_to_f128M(16, &sixteen); | |
float128_t point_5 = dbl_to_f128(0.5); | |
uint32_t x = 0; | |
float128_t y = zero; | |
float128_t scale = one; | |
float128_t bias = zero; | |
int gottail = 0, gotrad = 0, gotdig = 0; | |
long long rp = 0; | |
long long dc = 0; | |
long long e2 = 0; | |
int d; | |
int c; | |
c = shgetc(f); | |
/* Skip leading zeros/underscores */ | |
for (; c=='0' || c=='_'; c = shgetc(f)) gotdig = 1; | |
if (c=='.') { | |
gotrad = 1; | |
c = shgetc(f); | |
/* Count zeros after the radix point before significand */ | |
for (rp=0; ; c = shgetc(f)) { | |
if (c == '_') { | |
continue; | |
} else if (c == '0') { | |
gotdig = 1; | |
rp--; | |
} else { | |
break; | |
} | |
} | |
} | |
for (; c-'0'<10U || (c|32)-'a'<6U || c=='.' || c=='_'; c = shgetc(f)) { | |
if (c=='_') { | |
continue; | |
} else if (c=='.') { | |
if (gotrad) break; | |
rp = dc; | |
gotrad = 1; | |
} else { | |
gotdig = 1; | |
if (c > '9') d = (c|32)+10-'a'; | |
else d = c-'0'; | |
if (dc<8) { | |
x = x*16 + d; | |
} else if (dc < LDBL_MANT_DIG/4+1) { | |
//y += d*(scale/=16); | |
{ | |
float128_t divided; | |
f128M_div(&scale, &sixteen, ÷d); | |
scale = divided; | |
float128_t d_f128; | |
i32_to_f128M(d, &d_f128); | |
float128_t add_op; | |
f128M_mul(&d_f128, &scale, &add_op); | |
float128_t new_y; | |
f128M_add(&y, &add_op, &new_y); | |
y = new_y; | |
} | |
} else if (d && !gottail) { | |
//y += 0.5*scale; | |
{ | |
float128_t add_op; | |
f128M_mul(&point_5, &scale, &add_op); | |
float128_t new_y; | |
f128M_add(&y, &add_op, &new_y); | |
y = new_y; | |
} | |
gottail = 1; | |
} | |
dc++; | |
} | |
} | |
if (!gotdig) { | |
shunget(f); | |
if (pok) { | |
shunget(f); | |
if (gotrad) shunget(f); | |
} else { | |
shlim(f, 0); | |
} | |
//return sign * 0.0; | |
return dbl_to_f128(sign * 0.0); | |
} | |
if (!gotrad) rp = dc; | |
while (dc<8) x *= 16, dc++; | |
if ((c|32)=='p') { | |
e2 = scanexp(f, pok); | |
if (e2 == LLONG_MIN) { | |
if (pok) { | |
shunget(f); | |
} else { | |
shlim(f, 0); | |
return zero; | |
} | |
e2 = 0; | |
} | |
} else { | |
shunget(f); | |
} | |
e2 += 4*rp - 32; | |
if (!x) { | |
//return sign * 0.0; | |
return dbl_to_f128(sign * 0.0); | |
} | |
if (e2 > -emin) { | |
errno = ERANGE; | |
//return sign * LDBL_MAX * LDBL_MAX; | |
return zero; | |
} | |
if (e2 < emin-2*LDBL_MANT_DIG) { | |
errno = ERANGE; | |
//return sign * LDBL_MIN * LDBL_MIN; | |
return zero; | |
} | |
while (x < 0x80000000) { | |
//if (y>=0.5) | |
if (!f128M_lt(&y, &point_5)) { | |
x += x + 1; | |
//y += y - 1; | |
{ | |
float128_t minus_one; | |
f128M_sub(&y, &one, &minus_one); | |
float128_t new_y; | |
f128M_add(&y, &minus_one, &new_y); | |
y = new_y; | |
} | |
} else { | |
x += x; | |
//y += y; | |
{ | |
float128_t new_y; | |
f128M_add(&y, &y, &new_y); | |
y = new_y; | |
} | |
} | |
e2--; | |
} | |
if (bits > 32+e2-emin) { | |
bits = 32+e2-emin; | |
if (bits<0) bits=0; | |
} | |
if (bits < LDBL_MANT_DIG) { | |
float128_t sign_f128; | |
i32_to_f128M(sign, &sign_f128); | |
bias = copysignf128(dbl_to_f128(scalbn(1, 32+LDBL_MANT_DIG-bits-1)), sign_f128); | |
} | |
//if (bits<32 && y && !(x&1)) x++, y=0; | |
if (bits<32 && !f128M_eq(&y, &zero) && !(x&1)) x++, y=zero; | |
//y = bias + sign*(float128_t)x + sign*y; | |
{ | |
float128_t x_f128; | |
ui32_to_f128M(x, &x_f128); | |
float128_t sign_f128; | |
i32_to_f128M(sign, &sign_f128); | |
float128_t sign_mul_x; | |
f128M_mul(&sign_f128, &x_f128, &sign_mul_x); | |
float128_t sign_mul_y; | |
f128M_mul(&sign_f128, &y, &sign_mul_y); | |
float128_t bias_op; | |
f128M_add(&bias, &sign_mul_x, &bias_op); | |
float128_t new_y; | |
f128M_add(&bias_op, &sign_mul_y, &new_y); | |
y = new_y; | |
} | |
//y -= bias; | |
{ | |
float128_t new_y; | |
f128M_sub(&y, &bias, &new_y); | |
y = new_y; | |
} | |
if (f128M_eq(&y, &zero)) errno = ERANGE; | |
return scalbnf128(y, e2); | |
} | |
static int isspace(int c) | |
{ | |
return c == ' ' || (unsigned)c-'\t' < 5; | |
} | |
static inline float128_t makeInf128() { | |
union ldshape ux; | |
ux.i2.hi = 0x7fff000000000000UL; | |
ux.i2.lo = 0x0UL; | |
return ux.f; | |
} | |
static inline float128_t makeNaN128() { | |
uint64_t rand = 0UL; | |
union ldshape ux; | |
ux.i2.hi = 0x7fff000000000000UL | (rand & 0xffffffffffffUL); | |
ux.i2.lo = 0x0UL; | |
return ux.f; | |
} | |
float128_t __floatscan(struct MuslFILE *f, int prec, int pok) | |
{ | |
int sign = 1; | |
var i: isize = undefined; | |
int bits = LDBL_MANT_DIG; | |
int emin = LDBL_MIN_EXP-bits; | |
int c; | |
while (isspace((c=shgetc(f)))); | |
if (c=='+' || c=='-') { | |
sign -= 2*(c=='-'); | |
c = shgetc(f); | |
} | |
for (i=0; i<8 && (c|32)=="infinity"[i]; i++) | |
if (i<7) c = shgetc(f); | |
if (i==3 || i==8 || (i>3 && pok)) { | |
if (i!=8) { | |
shunget(f); | |
if (pok) for (; i>3; i--) shunget(f); | |
} | |
//return sign * INFINITY; | |
float128_t sign_f128; | |
i32_to_f128M(sign, &sign_f128); | |
float128_t infinity_f128 = makeInf128(); | |
float128_t result; | |
f128M_mul(&sign_f128, &infinity_f128, &result); | |
return result; | |
} | |
if (!i) for (i=0; i<3 && (c|32)=="nan"[i]; i++) | |
if (i<2) c = shgetc(f); | |
if (i==3) { | |
if (shgetc(f) != '(') { | |
shunget(f); | |
return makeNaN128(); | |
} | |
for (i=1; ; i++) { | |
c = shgetc(f); | |
if (c-'0'<10U || c-'A'<26U || c-'a'<26U || c=='_') | |
continue; | |
if (c==')') return makeNaN128(); | |
shunget(f); | |
if (!pok) { | |
errno = EINVAL; | |
shlim(f, 0); | |
float128_t zero; | |
ui32_to_f128M(0, &zero); | |
return zero; | |
} | |
while (i--) shunget(f); | |
return makeNaN128(); | |
} | |
return makeNaN128(); | |
} | |
if (i) { | |
shunget(f); | |
errno = EINVAL; | |
shlim(f, 0); | |
float128_t zero; | |
ui32_to_f128M(0, &zero); | |
return zero; | |
} | |
if (c=='0') { | |
c = shgetc(f); | |
if ((c|32) == 'x') | |
return hexfloat(f, bits, emin, sign, pok); | |
shunget(f); | |
c = '0'; | |
} | |
return decfloat(f, c, bits, emin, sign, pok); | |
} | |
float128_t parse_f128(const char *s, char **p) { | |
struct MuslFILE f; | |
sh_fromstring(&f, s); | |
shlim(&f, 0); | |
float128_t y = __floatscan(&f, 2, 1); | |
var cnt: isize = shcnt(&f); | |
if (p) *p = cnt ? (char *)s + cnt : (char *)s; | |
return y; | |
} | |
// TODO delete stuff below | |
// only keep it for now for looking up libstd functions | |
// The mantissa field in FloatRepr is 64bit wide and holds only 19 digits | |
// without overflowing | |
const max_digits = 19; | |
const f64_plus_zero: u64 = 0x0000000000000000; | |
const f64_minus_zero: u64 = 0x8000000000000000; | |
const f64_plus_infinity: u64 = 0x7FF0000000000000; | |
const f64_minus_infinity: u64 = 0xFFF0000000000000; | |
const Z96 = struct { | |
d0: u32, | |
d1: u32, | |
d2: u32, | |
// d = s >> 1 | |
inline fn shiftRight1(d: *Z96, s: Z96) void { | |
d.d0 = (s.d0 >> 1) | ((s.d1 & 1) << 31); | |
d.d1 = (s.d1 >> 1) | ((s.d2 & 1) << 31); | |
d.d2 = s.d2 >> 1; | |
} | |
// d = s << 1 | |
inline fn shiftLeft1(d: *Z96, s: Z96) void { | |
d.d2 = (s.d2 << 1) | ((s.d1 & (1 << 31)) >> 31); | |
d.d1 = (s.d1 << 1) | ((s.d0 & (1 << 31)) >> 31); | |
d.d0 = s.d0 << 1; | |
} | |
// d += s | |
inline fn add(d: *Z96, s: Z96) void { | |
var w = @as(u64, d.d0) + @as(u64, s.d0); | |
d.d0 = @truncate(u32, w); | |
w >>= 32; | |
w += @as(u64, d.d1) + @as(u64, s.d1); | |
d.d1 = @truncate(u32, w); | |
w >>= 32; | |
w += @as(u64, d.d2) + @as(u64, s.d2); | |
d.d2 = @truncate(u32, w); | |
} | |
// d -= s | |
inline fn sub(d: *Z96, s: Z96) void { | |
var w = @as(u64, d.d0) -% @as(u64, s.d0); | |
d.d0 = @truncate(u32, w); | |
w >>= 32; | |
w += @as(u64, d.d1) -% @as(u64, s.d1); | |
d.d1 = @truncate(u32, w); | |
w >>= 32; | |
w += @as(u64, d.d2) -% @as(u64, s.d2); | |
d.d2 = @truncate(u32, w); | |
} | |
}; | |
const FloatRepr = struct { | |
negative: bool, | |
exponent: i32, | |
mantissa: u64, | |
}; | |
fn convertRepr(comptime T: type, n: FloatRepr) T { | |
const mask28: u32 = 0xf << 28; | |
var s: Z96 = undefined; | |
var q: Z96 = undefined; | |
var r: Z96 = undefined; | |
s.d0 = @truncate(u32, n.mantissa); | |
s.d1 = @truncate(u32, n.mantissa >> 32); | |
s.d2 = 0; | |
var binary_exponent: i32 = 92; | |
var exp = n.exponent; | |
while (exp > 0) : (exp -= 1) { | |
q.shiftLeft1(s); // q = p << 1 | |
r.shiftLeft1(q); // r = p << 2 | |
s.shiftLeft1(r); // p = p << 3 | |
s.add(q); // p = (p << 3) + (p << 1) | |
while (s.d2 & mask28 != 0) { | |
q.shiftRight1(s); | |
binary_exponent += 1; | |
s = q; | |
} | |
} | |
while (exp < 0) { | |
while (s.d2 & (1 << 31) == 0) { | |
q.shiftLeft1(s); | |
binary_exponent -= 1; | |
s = q; | |
} | |
q.d2 = s.d2 / 10; | |
r.d1 = s.d2 % 10; | |
r.d2 = (s.d1 >> 8) | (r.d1 << 24); | |
q.d1 = r.d2 / 10; | |
r.d1 = r.d2 % 10; | |
r.d2 = ((s.d1 & 0xff) << 16) | (s.d0 >> 16) | (r.d1 << 24); | |
r.d0 = r.d2 / 10; | |
r.d1 = r.d2 % 10; | |
q.d1 = (q.d1 << 8) | ((r.d0 & 0x00ff0000) >> 16); | |
q.d0 = r.d0 << 16; | |
r.d2 = (s.d0 *% 0xffff) | (r.d1 << 16); | |
q.d0 |= r.d2 / 10; | |
s = q; | |
exp += 1; | |
} | |
if (s.d0 != 0 or s.d1 != 0 or s.d2 != 0) { | |
while (s.d2 & mask28 == 0) { | |
q.shiftLeft1(s); | |
binary_exponent -= 1; | |
s = q; | |
} | |
} | |
binary_exponent += 1023; | |
const repr: u64 = blk: { | |
if (binary_exponent > 2046) { | |
break :blk if (n.negative) f64_minus_infinity else f64_plus_infinity; | |
} else if (binary_exponent < 1) { | |
break :blk if (n.negative) f64_minus_zero else f64_plus_zero; | |
} else if (s.d2 != 0) { | |
const binexs2 = @intCast(u64, binary_exponent) << 52; | |
const rr = (@as(u64, s.d2 & ~mask28) << 24) | ((@as(u64, s.d1) + 128) >> 8) | binexs2; | |
break :blk if (n.negative) rr | (1 << 63) else rr; | |
} else { | |
break :blk 0; | |
} | |
}; | |
const f = @bitCast(f64, repr); | |
return @floatCast(T, f); | |
} | |
const State = enum { | |
MaybeSign, | |
LeadingMantissaZeros, | |
LeadingFractionalZeros, | |
MantissaIntegral, | |
MantissaFractional, | |
ExponentSign, | |
LeadingExponentZeros, | |
Exponent, | |
}; | |
const ParseResult = enum { | |
Ok, | |
PlusZero, | |
MinusZero, | |
PlusInf, | |
MinusInf, | |
}; | |
fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult { | |
var digit_index: usize = 0; | |
var negative_exp = false; | |
var exponent: i32 = 0; | |
var state = State.MaybeSign; | |
var i: usize = 0; | |
while (i < s.len) { | |
const c = s[i]; | |
switch (state) { | |
.MaybeSign => { | |
state = .LeadingMantissaZeros; | |
if (c == '+') { | |
i += 1; | |
} else if (c == '-') { | |
n.negative = true; | |
i += 1; | |
} else if (ascii.isDigit(c) or c == '.') { | |
// continue | |
} else { | |
return error.InvalidCharacter; | |
} | |
}, | |
.LeadingMantissaZeros => { | |
if (c == '0') { | |
i += 1; | |
} else if (c == '.') { | |
i += 1; | |
state = .LeadingFractionalZeros; | |
} else if (c == '_') { | |
i += 1; | |
} else { | |
state = .MantissaIntegral; | |
} | |
}, | |
.LeadingFractionalZeros => { | |
if (c == '0') { | |
i += 1; | |
if (n.exponent > std.math.minInt(i32)) { | |
n.exponent -= 1; | |
} | |
} else { | |
state = .MantissaFractional; | |
} | |
}, | |
.MantissaIntegral => { | |
if (ascii.isDigit(c)) { | |
if (digit_index < max_digits) { | |
n.mantissa *%= 10; | |
n.mantissa += c - '0'; | |
digit_index += 1; | |
} else if (n.exponent < std.math.maxInt(i32)) { | |
n.exponent += 1; | |
} | |
i += 1; | |
} else if (c == '.') { | |
i += 1; | |
state = .MantissaFractional; | |
} else if (c == '_') { | |
i += 1; | |
} else { | |
state = .MantissaFractional; | |
} | |
}, | |
.MantissaFractional => { | |
if (ascii.isDigit(c)) { | |
if (digit_index < max_digits) { | |
n.mantissa *%= 10; | |
n.mantissa += c - '0'; | |
n.exponent -%= 1; | |
digit_index += 1; | |
} | |
i += 1; | |
} else if (c == 'e' or c == 'E') { | |
i += 1; | |
state = .ExponentSign; | |
} else if (c == '_') { | |
i += 1; | |
} else { | |
state = .ExponentSign; | |
} | |
}, | |
.ExponentSign => { | |
if (c == '+') { | |
i += 1; | |
} else if (c == '_') { | |
return error.InvalidCharacter; | |
} else if (c == '-') { | |
negative_exp = true; | |
i += 1; | |
} | |
state = .LeadingExponentZeros; | |
}, | |
.LeadingExponentZeros => { | |
if (c == '0') { | |
i += 1; | |
} else if (c == '_') { | |
i += 1; | |
} else { | |
state = .Exponent; | |
} | |
}, | |
.Exponent => { | |
if (ascii.isDigit(c)) { | |
if (exponent < std.math.maxInt(i32) / 10) { | |
exponent *= 10; | |
exponent += @intCast(i32, c - '0'); | |
} | |
i += 1; | |
} else if (c == '_') { | |
i += 1; | |
} else { | |
return error.InvalidCharacter; | |
} | |
}, | |
} | |
} | |
if (negative_exp) exponent = -exponent; | |
n.exponent += exponent; | |
if (n.mantissa == 0) { | |
return if (n.negative) .MinusZero else .PlusZero; | |
} else if (n.exponent > 309) { | |
return if (n.negative) .MinusInf else .PlusInf; | |
} else if (n.exponent < -328) { | |
return if (n.negative) .MinusZero else .PlusZero; | |
} | |
return .Ok; | |
} | |
fn caseInEql(a: []const u8, b: []const u8) bool { | |
if (a.len != b.len) return false; | |
for (a) |_, i| { | |
if (ascii.toUpper(a[i]) != ascii.toUpper(b[i])) { | |
return false; | |
} | |
} | |
return true; | |
} | |
pub const ParseFloatError = error{InvalidCharacter}; | |
pub fn parseFloat(comptime T: type, s: []const u8) ParseFloatError!T { | |
if (s.len == 0 or (s.len == 1 and (s[0] == '+' or s[0] == '-'))) { | |
return error.InvalidCharacter; | |
} | |
if (caseInEql(s, "nan")) { | |
return std.math.nan(T); | |
} else if (caseInEql(s, "inf") or caseInEql(s, "+inf")) { | |
return std.math.inf(T); | |
} else if (caseInEql(s, "-inf")) { | |
return -std.math.inf(T); | |
} | |
var r = FloatRepr{ | |
.negative = false, | |
.exponent = 0, | |
.mantissa = 0, | |
}; | |
return switch (try parseRepr(s, &r)) { | |
.Ok => convertRepr(T, r), | |
.PlusZero => 0.0, | |
.MinusZero => -@as(T, 0.0), | |
.PlusInf => std.math.inf(T), | |
.MinusInf => -std.math.inf(T), | |
}; | |
} | |
test "fmt.parseFloat" { | |
const testing = std.testing; | |
const expect = testing.expect; | |
const expectEqual = testing.expectEqual; | |
const approxEqAbs = std.math.approxEqAbs; | |
const epsilon = 1e-7; | |
inline for ([_]type{ f16, f32, f64, f128 }) |T| { | |
const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); | |
try testing.expectError(error.InvalidCharacter, parseFloat(T, "")); | |
try testing.expectError(error.InvalidCharacter, parseFloat(T, " 1")); | |
try testing.expectError(error.InvalidCharacter, parseFloat(T, "1abc")); | |
try testing.expectError(error.InvalidCharacter, parseFloat(T, "+")); | |
try testing.expectError(error.InvalidCharacter, parseFloat(T, "-")); | |
try expectEqual(try parseFloat(T, "0"), 0.0); | |
try expectEqual(try parseFloat(T, "0"), 0.0); | |
try expectEqual(try parseFloat(T, "+0"), 0.0); | |
try expectEqual(try parseFloat(T, "-0"), 0.0); | |
try expectEqual(try parseFloat(T, "0e0"), 0); | |
try expectEqual(try parseFloat(T, "2e3"), 2000.0); | |
try expectEqual(try parseFloat(T, "1e0"), 1.0); | |
try expectEqual(try parseFloat(T, "-2e3"), -2000.0); | |
try expectEqual(try parseFloat(T, "-1e0"), -1.0); | |
try expectEqual(try parseFloat(T, "1.234e3"), 1234); | |
try expect(approxEqAbs(T, try parseFloat(T, "3.141"), 3.141, epsilon)); | |
try expect(approxEqAbs(T, try parseFloat(T, "-3.141"), -3.141, epsilon)); | |
try expectEqual(try parseFloat(T, "1e-700"), 0); | |
try expectEqual(try parseFloat(T, "1e+700"), std.math.inf(T)); | |
try expectEqual(@bitCast(Z, try parseFloat(T, "nAn")), @bitCast(Z, std.math.nan(T))); | |
try expectEqual(try parseFloat(T, "inF"), std.math.inf(T)); | |
try expectEqual(try parseFloat(T, "-INF"), -std.math.inf(T)); | |
try expectEqual(try parseFloat(T, "0.4e0066999999999999999999999999999999999999999999999999999"), std.math.inf(T)); | |
try expect(approxEqAbs(T, try parseFloat(T, "0_1_2_3_4_5_6.7_8_9_0_0_0e0_0_1_0"), @as(T, 123456.789000e10), epsilon)); | |
if (T != f16) { | |
try expect(approxEqAbs(T, try parseFloat(T, "1e-2"), 0.01, epsilon)); | |
try expect(approxEqAbs(T, try parseFloat(T, "1234e-2"), 12.34, epsilon)); | |
try expect(approxEqAbs(T, try parseFloat(T, "123142.1"), 123142.1, epsilon)); | |
try expect(approxEqAbs(T, try parseFloat(T, "-123142.1124"), @as(T, -123142.1124), epsilon)); | |
try expect(approxEqAbs(T, try parseFloat(T, "0.7062146892655368"), @as(T, 0.7062146892655368), epsilon)); | |
try expect(approxEqAbs(T, try parseFloat(T, "2.71828182845904523536"), @as(T, 2.718281828459045), epsilon)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment