commit 1849e53c71b71422a7f4b28a3a287ec7a772279e
parent 699fb637b93d19e19cbd97e593fb135685f4406d
Author: Sudipto Mallick <smlckz@disroot.org>
Date: Thu, 8 Jul 2021 16:35:24 +0000
math/floats.ha: functions and constants related to floats
Signed-off-by: Sudipto Mallick <smlckz@disroot.org>
Diffstat:
4 files changed, 290 insertions(+), 30 deletions(-)
diff --git a/math/floats.ha b/math/floats.ha
@@ -0,0 +1,230 @@
+// The floating point value representing Not a Number, i.e. an undefined or
+// unrepresentable value. You cannot test if a number is NaN by comparing to
+// this value; see [[isnan]] instead.
+export def NAN: f32 = 0.0 / 0.0;
+
+// The floating point value representing positive infinity. Use -[[INF]] for
+// negative infinity.
+export def INF: f32 = 1.0 / 0.0;
+
+// Returns true if the given floating-point number is NaN.
+export fn isnan(n: f64) bool = n != n;
+
+@test fn isnan() void = {
+ assert(isnan(NAN));
+ assert(isnan(-NAN));
+ assert(isnan(f64frombits(0xfffabcdef1234567)));
+ assert(!isnan(INF));
+ assert(!isnan(1.23f32));
+};
+
+// Returns true if the given floating-point number is infinite.
+export fn isinf(n: f64) bool = {
+ const bits = f64bits(n);
+ const mant = bits & F64_MANTISSA_MASK;
+ const exp = bits >> F64_MANTISSA_BITS & F64_EXPONENT_MASK;
+ return exp == F64_EXPONENT_MASK && mant == 0;
+};
+
+@test fn isinf() void = {
+ assert(isinf(INF));
+ assert(isinf(-INF));
+ assert(!isinf(NAN));
+ assert(!isinf(1.23));
+ assert(!isinf(-1.23f32));
+};
+
+// Returns true if the given floating-point number is normal.
+export fn isnormal(n: (f32 | f64)) bool = {
+ return match (n) {
+ n: f32 => isnormalf32(n),
+ n: f64 => isnormalf64(n),
+ };
+};
+
+// Returns true if the given f32 is normal.
+export fn isnormalf32(n: f32) bool = {
+ const bits = f32bits(n);
+ const mant = bits & F32_MANTISSA_MASK;
+ const exp = bits >> F32_MANTISSA_BITS & F32_EXPONENT_MASK;
+ return exp != F32_EXPONENT_MASK && (exp > 0 || mant == 0);
+};
+
+// Returns true if the given f64 is normal.
+export fn isnormalf64(n: f64) bool = {
+ const bits = f64bits(n);
+ const mant = bits & F64_MANTISSA_MASK;
+ const exp = bits >> F64_MANTISSA_BITS & F64_EXPONENT_MASK;
+ return exp != F64_EXPONENT_MASK && (exp > 0 || mant == 0);
+};
+
+// Returns true if the given floating-point number is subnormal.
+export fn issubnormal(n: (f32 | f64)) bool = {
+ return match (n) {
+ n: f32 => issubnormalf32(n),
+ n: f64 => issubnormalf64(n),
+ };
+};
+
+// Returns true if the given f32 is subnormal.
+export fn issubnormalf32(n: f32) bool = {
+ const bits = f32bits(n);
+ const mant = bits & F32_MANTISSA_MASK;
+ const exp = bits >> F32_MANTISSA_BITS & F32_EXPONENT_MASK;
+ return exp == 0 && mant != 0;
+};
+
+// Returns true if the given f64 is subnormal.
+export fn issubnormalf64(n: f64) bool = {
+ const bits = f64bits(n);
+ const mant = bits & F64_MANTISSA_MASK;
+ const exp = bits >> F64_MANTISSA_BITS & F64_EXPONENT_MASK;
+ return exp == 0 && mant != 0;
+};
+
+@test fn float_normality() void = {
+ assert(isnormal(0.0));
+ assert(isnormal(1.0));
+ assert(!isnormal(NAN));
+ assert(!isnormal(INF));
+ assert(!isnormal(1.0e-310));
+ assert(!isnormal(1.0e-40f32));
+
+ assert(isnormalf32(1.0));
+ assert(isnormalf32(0.0));
+ assert(!isnormalf32(NAN));
+ assert(!isnormalf32(INF));
+ assert(!isnormalf32(-1.0e-40));
+ assert(isnormalf32(-1.0e-50));
+
+ assert(isnormalf64(1.0));
+ assert(isnormalf64(0.0));
+ assert(!isnormalf64(NAN));
+ assert(!isnormalf64(INF));
+ assert(!isnormalf64(-1.0e-320));
+ assert(isnormalf64(-1.0e-330));
+
+ assert(issubnormal(1.0e-320));
+ assert(issubnormal(1.0e-42f32));
+ assert(!issubnormal(NAN));
+ assert(!issubnormal(INF));
+ assert(!issubnormal(1.0));
+ assert(!issubnormal(0.0));
+
+ assert(issubnormalf32(1.0e-45));
+ assert(issubnormalf32(-1.0e-39));
+ assert(!issubnormalf32(-NAN));
+ assert(!issubnormalf32(-INF));
+ assert(!issubnormalf32(0.0));
+ assert(!issubnormalf32(-1.0e-49));
+
+ assert(issubnormalf64(5.0e-324));
+ assert(issubnormalf64(-2.0e-310));
+ assert(!issubnormalf64(-NAN));
+ assert(!issubnormalf64(-INF));
+ assert(!issubnormalf64(-1.0e-400));
+ assert(!issubnormalf64(0.0));
+};
+
+// Returns the binary representation of the given f64.
+export fn f64bits(n: f64) u64 = *(&n: *u64);
+
+// Returns the binary representation of the given f32.
+export fn f32bits(n: f32) u32 = *(&n: *u32);
+
+// Returns f64 with the given binary representation.
+export fn f64frombits(n: u64) f64 = *(&n: *f64);
+
+// Returns f32 with the given binary representation.
+export fn f32frombits(n: u32) f32 = *(&n: *f32);
+
+@test fn floatbits() void = {
+ const a: [_]f64 = [INF, -INF, 0.0, 1.0, -1.0, 123456789.0,
+ F64_MIN, F64_MIN_NORMAL, F64_MAX_NORMAL];
+ for (let i = 0z; i < len(a); i += 1) {
+ assert(f64frombits(f64bits(a[i])) == a[i]);
+ };
+ const a: [_]f32 = [INF, -INF, 0.0, 1.0, -1.0, -123456.0,
+ F32_MIN, F32_MIN_NORMAL, F32_MAX_NORMAL];
+ for (let i = 0z; i < len(a); i += 1) {
+ assert(f32frombits(f32bits(a[i])) == a[i]);
+ };
+};
+
+// The number of bits in the significand of the binary representation of f64.
+export def F64_MANTISSA_BITS: u64 = 52;
+
+// The number of bits in the exponent of the binary representation of f64.
+export def F64_EXPONENT_BITS: u64 = 11;
+
+// The bias of the exponent of the binary representation of f64. Subtract this
+// from the exponent in the binary representation to get the actual exponent.
+export def F64_EXPONENT_BIAS: u16 = 1023;
+
+// The number of bits in the significand of the binary representation of f32.
+export def F32_MANTISSA_BITS: u64 = 23;
+
+// The number of bits in the exponent of the binary representation of f32.
+export def F32_EXPONENT_BITS: u64 = 8;
+
+// The bias of the exponent of the binary representation of f32. Subtract this
+// from the exponent in the binary representation to get the actual exponent.
+export def F32_EXPONENT_BIAS: u16 = 127;
+
+def F64_MANTISSA_MASK: u64 = (1 << F64_MANTISSA_BITS) - 1;
+def F64_EXPONENT_MASK: u64 = (1 << F64_EXPONENT_BITS) - 1;
+
+def F32_MANTISSA_MASK: u64 = (1 << F32_MANTISSA_BITS) - 1;
+def F32_EXPONENT_MASK: u64 = (1 << F32_EXPONENT_BITS) - 1;
+
+// The largest representable f64 value which is less than Infinity.
+export def F64_MAX_NORMAL: f64 = 1.7976931348623157e+308;
+
+// The smallest representable normal f64 value.
+export def F64_MIN_NORMAL: f64 = 2.2250738585072014e-308;
+
+// THe smallest (subnormal) f64 value greater than zero.
+export def F64_MIN: f64 = 5.0e-324;
+
+// The largest representable f32 value which is less than Infinity.
+export def F32_MAX_NORMAL: f32 = 3.4028234e+38;
+
+// The smallest representable normal f32 value.
+export def F32_MIN_NORMAL: f32 = 1.1754944e-38;
+
+// The smallest (subnormal) f32 value greater than zero.
+export def F32_MIN: f32 = 1.0e-45;
+
+// Contains information about the structure of a specific floating point number
+// type.
+export type floatinfo = struct {
+ // Bits in significand.
+ mantbits: u64,
+ // Bits in exponent.
+ expbits: u64,
+ // Bias of exponent.
+ expbias: int,
+ // Mask for mantissa.
+ mantmask: u64,
+ // Mask for exponent.
+ expmask: u64,
+};
+
+// A [[floatinfo]] structure defining the structure of the f64 type.
+export const f64info: floatinfo = floatinfo {
+ mantbits = 52,
+ expbits = 11,
+ expbias = 1023,
+ mantmask = (1 << 52) - 1,
+ expmask = (1 << 11) - 1,
+};
+
+// A [[floatinfo]] structure defining the structure of the f32 type.
+export const f32info: floatinfo = floatinfo {
+ mantbits = 23,
+ expbits = 8,
+ expbias = 127,
+ mantmask = (1 << 23) - 1,
+ expmask = (1 << 8) - 1,
+};
+
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -541,6 +541,13 @@ linux_vdso() {
gen_ssa linux::vdso linux strings format::elf
}
+math() {
+ printf '# math\n'
+ gen_srcs math \
+ floats.ha
+ gen_ssa math
+}
+
net() {
printf '# net\n'
gen_srcs net \
@@ -704,7 +711,7 @@ strconv() {
+test/stou.ha \
+test/stoi.ha
fi
- gen_ssa strconv types strings ascii
+ gen_ssa strconv types strings ascii math
}
strings() {
@@ -846,6 +853,7 @@ linux
linux::signalfd
linux::io_uring
linux::vdso
+math
net
net::dial
net::dns
diff --git a/stdlib.mk b/stdlib.mk
@@ -241,6 +241,10 @@ hare_stdlib_deps+=$(stdlib_linux_io_uring)
stdlib_linux_vdso=$(HARECACHE)/linux/vdso/linux_vdso.o
hare_stdlib_deps+=$(stdlib_linux_vdso)
+# gen_lib math
+stdlib_math=$(HARECACHE)/math/math.o
+hare_stdlib_deps+=$(stdlib_math)
+
# gen_lib net
stdlib_net=$(HARECACHE)/net/net.o
hare_stdlib_deps+=$(stdlib_net)
@@ -819,6 +823,17 @@ $(HARECACHE)/linux/vdso/linux_vdso.ssa: $(stdlib_linux_vdso_srcs) $(stdlib_rt) $
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nlinux::vdso \
-t$(HARECACHE)/linux/vdso/linux_vdso.td $(stdlib_linux_vdso_srcs)
+# math
+# math
+stdlib_math_srcs= \
+ $(STDLIB)/math/floats.ha
+
+$(HARECACHE)/math/math.ssa: $(stdlib_math_srcs) $(stdlib_rt)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(HARECACHE)/math
+ @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nmath \
+ -t$(HARECACHE)/math/math.td $(stdlib_math_srcs)
+
# net
# net
stdlib_net_srcs= \
@@ -999,7 +1014,7 @@ stdlib_strconv_srcs= \
$(STDLIB)/strconv/ftos.ha \
$(STDLIB)/strconv/stof.ha
-$(HARECACHE)/strconv/strconv.ssa: $(stdlib_strconv_srcs) $(stdlib_rt) $(stdlib_types) $(stdlib_strings) $(stdlib_ascii)
+$(HARECACHE)/strconv/strconv.ssa: $(stdlib_strconv_srcs) $(stdlib_rt) $(stdlib_types) $(stdlib_strings) $(stdlib_ascii) $(stdlib_math)
@printf 'HAREC \t$@\n'
@mkdir -p $(HARECACHE)/strconv
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nstrconv \
@@ -1392,6 +1407,10 @@ hare_testlib_deps+=$(testlib_linux_io_uring)
testlib_linux_vdso=$(TESTCACHE)/linux/vdso/linux_vdso.o
hare_testlib_deps+=$(testlib_linux_vdso)
+# gen_lib math
+testlib_math=$(TESTCACHE)/math/math.o
+hare_testlib_deps+=$(testlib_math)
+
# gen_lib net
testlib_net=$(TESTCACHE)/net/net.o
hare_testlib_deps+=$(testlib_net)
@@ -1990,6 +2009,17 @@ $(TESTCACHE)/linux/vdso/linux_vdso.ssa: $(testlib_linux_vdso_srcs) $(testlib_rt)
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nlinux::vdso \
-t$(TESTCACHE)/linux/vdso/linux_vdso.td $(testlib_linux_vdso_srcs)
+# math
+# math
+testlib_math_srcs= \
+ $(STDLIB)/math/floats.ha
+
+$(TESTCACHE)/math/math.ssa: $(testlib_math_srcs) $(testlib_rt)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(TESTCACHE)/math
+ @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nmath \
+ -t$(TESTCACHE)/math/math.td $(testlib_math_srcs)
+
# net
# net
testlib_net_srcs= \
@@ -2174,7 +2204,7 @@ testlib_strconv_srcs= \
$(STDLIB)/strconv/+test/stou.ha \
$(STDLIB)/strconv/+test/stoi.ha
-$(TESTCACHE)/strconv/strconv.ssa: $(testlib_strconv_srcs) $(testlib_rt) $(testlib_types) $(testlib_strings) $(testlib_ascii)
+$(TESTCACHE)/strconv/strconv.ssa: $(testlib_strconv_srcs) $(testlib_rt) $(testlib_types) $(testlib_strings) $(testlib_ascii) $(testlib_math)
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/strconv
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nstrconv \
diff --git a/strconv/ftos.ha b/strconv/ftos.ha
@@ -3,11 +3,9 @@
// This Hare implementation is translated from the original
// C implementation here: https://github.com/ulfjack/ryu
+use math;
use types;
-fn f64bits(a: f64) u64 = *(&a: *u64); // XXX: ARCH
-fn f32bits(a: f32) u32 = *(&a: *u32); // XXX: ARCH
-
type r128 = struct {
hi: u64,
lo: u64,
@@ -285,19 +283,15 @@ type decf64 = struct {
exponent: i32,
};
-def F64_MANTISSA_BITS: u64 = 52;
-def F64_EXPONENT_BITS: u64 = 11;
-def F64_EXPONENT_BIAS: u16 = 1023;
-
fn f64todecf64(mantissa: u64, exponent: u32) decf64 = {
- let e2 = (F64_EXPONENT_BIAS + F64_MANTISSA_BITS + 2): i32;
+ let e2 = (math::F64_EXPONENT_BIAS + math::F64_MANTISSA_BITS + 2): i32;
let m2: u64 = 0;
if (exponent == 0) {
e2 = 1 - e2;
m2 = mantissa;
} else {
e2 = (exponent: i32) - e2;
- m2 = (1u32 << F64_MANTISSA_BITS) | mantissa;
+ m2 = (1u64 << math::F64_MANTISSA_BITS) | mantissa;
};
const accept_bounds = (m2 & 1) == 0;
const mv = 4 * m2;
@@ -407,19 +401,15 @@ type decf32 = struct {
exponent: i32,
};
-def F32_MANTISSA_BITS: u32 = 23;
-def F32_EXPONENT_BITS: u32 = 8;
-def F32_EXPONENT_BIAS: u16 = 127;
-
fn f32todecf32(mantissa: u32, exponent: u32) decf32 = {
- let e2 = (F32_EXPONENT_BIAS + F32_MANTISSA_BITS + 2): i32;
+ let e2 = (math::F32_EXPONENT_BIAS + math::F32_MANTISSA_BITS + 2): i32;
let m2: u32 = 0;
if (exponent == 0) {
e2 = 1 - e2;
m2 = mantissa;
} else {
e2 = (exponent: i32) - e2;
- m2 = (1u32 << F32_MANTISSA_BITS) | mantissa;
+ m2 = (1u32 << math::F32_MANTISSA_BITS: u32) | mantissa;
};
const accept_bounds = (m2 & 1) == 0;
const mv = 4 * m2, mp = mv + 2;
@@ -629,14 +619,15 @@ export fn f64tos(n: f64) const str = {
// sign and the maximum of three digits for exponent.
// (1 + 1 + 1 + 16 + 1 + 1 + 3) = 24
static let buf: [24]u8 = [0...];
- const bits = f64bits(n);
- const sign = (bits >> (F64_MANTISSA_BITS + F64_EXPONENT_BITS)): size;
- const mantissa = bits & ((1u64 << F64_MANTISSA_BITS) - 1);
- const exponent = ((bits >> F64_MANTISSA_BITS) &
- (1u64 << F64_EXPONENT_BITS) - 1): u32;
+ const bits = math::f64bits(n);
+ const sign = (bits >> (math::F64_MANTISSA_BITS +
+ math::F64_EXPONENT_BITS)): size; // bits >> 63
+ const mantissa = bits & ((1u64 << math::F64_MANTISSA_BITS) - 1);
+ const exponent = ((bits >> math::F64_MANTISSA_BITS) &
+ (1u64 << math::F64_EXPONENT_BITS) - 1): u32;
if (mantissa == 0 && exponent == 0) {
return "0";
- } else if (exponent == ((1 << F64_EXPONENT_BITS) - 1)) {
+ } else if (exponent == ((1 << math::F64_EXPONENT_BITS) - 1)) {
if (mantissa != 0) {
return "NaN";
};
@@ -662,14 +653,15 @@ export fn f32tos(n: f32) const str = {
// the maximum of two digits for exponent.
// (1 + 1 + 1 + 7 + 1 + 1 + 2) = 14
static let buf: [16]u8 = [0...];
- const bits = f32bits(n);
- const sign = bits >> (F32_MANTISSA_BITS + F32_EXPONENT_BITS);
- const mantissa = bits & ((1u32 << F32_MANTISSA_BITS) - 1);
- const exponent = (bits >> F32_MANTISSA_BITS) &
- ((1u32 << F32_EXPONENT_BITS) - 1);
+ const bits = math::f32bits(n);
+ const sign = (bits >> (math::F32_MANTISSA_BITS +
+ math::F32_EXPONENT_BITS)): size; // bits >> 31
+ const mantissa = bits & ((1u32 << math::F32_MANTISSA_BITS) - 1): u32;
+ const exponent = (bits >> math::F32_MANTISSA_BITS): u32 &
+ ((1u32 << math::F32_EXPONENT_BITS) - 1): u32;
if (mantissa == 0 && exponent == 0) {
return "0";
- } else if (exponent == ((1 << F32_EXPONENT_BITS) - 1)) {
+ } else if (exponent == ((1 << math::F32_EXPONENT_BITS) - 1): u32) {
if (mantissa != 0) {
return "NaN";
};