about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-05-11 20:13:16 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-05-11 20:13:16 -0700
commita3d52c9028312706b45497c6b2205b8b165ef348 (patch)
treeb075fe9adbe9932c8ee3ed1fd28042d40815407b
parent25be3a05095e295b0ad3d3f7e5911ccee62c8f1a (diff)
downloadmu-a3d52c9028312706b45497c6b2205b8b165ef348.tar.gz
1349 - snapshot: floating-point support
-rw-r--r--021arithmetic.cc139
1 files changed, 139 insertions, 0 deletions
diff --git a/021arithmetic.cc b/021arithmetic.cc
index 307bcb18..d36b15f3 100644
--- a/021arithmetic.cc
+++ b/021arithmetic.cc
@@ -227,3 +227,142 @@ recipe main [
 +mem: storing 2 in location 3
 +run: product 1 is 4
 +mem: storing 5 in location 4
+
+//:: Support for non-integer numbers.
+
+//: Supporting non-integers is hopefully the only place where we need to think
+//: about the size of each location of memory.
+:(after "int main")
+assert(sizeof(long long int) == 8);
+assert(sizeof(double) == 8);
+
+:(before "End Globals")
+//: Conventional hardware uses the most significant bit to represent the sign
+//: in both (2's complement) integers, and so-called floating-point numbers
+//: (with sign, exponent and fraction regions: https://en.wikipedia.org/wiki/Double-precision_floating-point_format)
+static const long long int HOST_SET_NEGATIVE = 0x8000000000000000LL;
+//: Floating-point numbers store the sign of the exponent in the second-most
+//: significant bit.
+static const long long int HOST_SET_FLOAT_NEGATIVE_EXPONENT = 0x4000000000000000LL;
+
+//: As an experiment, we'd like to not have to distinguish between the two in
+//: mu. So we'll use the most-significant bit to represent whether a number is
+//: an integer (MSB 0) or a float (MSB 1). This will halve the set of numbers
+//: we can represent, whether integers or non-integers, but that price seems
+//: reasonable right now.
+const long long int MU_NUMBER_TYPE_MASK = 0x8000000000000000LL;
+//: As a result, the sign bit is now pushed to the second-most significant
+//: bit..
+const long long int MU_NUMBER_SIGN_MASK = 0x4000000000000000LL;
+//: ..and the sign of the exponent for floating-point is pushed to the
+//: third-most significant bit.
+const long long int MU_FLOAT_EXPONENT_SIGN_MASK = 0x2000000000000000LL;
+
+//: One nice side-effect of this mergek
+
+:(after "int main")
+assert(MU_NUMBER_TYPE_MASK == HOST_SET_NEGATIVE);
+assert(MU_NUMBER_SIGN_MASK == HOST_SET_FLOAT_NEGATIVE_EXPONENT);
+
+:(code)
+inline bool is_float(long long int number) {
+  return number & MU_NUMBER_TYPE_MASK;
+}
+
+inline bool is_integer(long long int number) {
+  return !is_float(number);
+}
+
+// both floats and integers use the most significant bit for the sign
+inline bool is_negative(long long int number) {
+  return number & MU_NUMBER_SIGN_MASK;
+}
+
+inline bool float_has_negative_exponent(long long int number) {
+  return number & MU_FLOAT_EXPONENT_SIGN_MASK;
+}
+
+long long int to_integer(long long int number) {
+  assert(is_integer(number));
+  if (is_negative(number)) {
+    // slide the sign over by one bit
+    number = number | HOST_SET_NEGATIVE;
+    // clear the old sign bit
+    number = number & (~MU_NUMBER_SIGN_MASK);
+  }
+  return number;
+}
+
+double to_float(long long int number) {
+  assert(is_float(number));
+//?   cerr << "0: " << std::hex << number << std::dec << ' ' << *reinterpret_cast<double*>(&number) << '\n'; //? 1
+  if (is_negative(number)) {
+    // slide the sign over by one bit
+    number = number | HOST_SET_NEGATIVE;
+//?     cerr << "1: " << std::hex << number << std::dec << ' ' << *reinterpret_cast<double*>(&number) << '\n'; //? 1
+    // clear the old sign bit
+    number = number & (~MU_NUMBER_SIGN_MASK);
+//?     cerr << "2: " << std::hex << number << std::dec << ' ' << *reinterpret_cast<double*>(&number) << '\n'; //? 1
+  }
+  if (float_has_negative_exponent(number)) {
+    // slide the sign of the exponent over by one bit
+    number = number | HOST_SET_FLOAT_NEGATIVE_EXPONENT;
+//?     cerr << "3: " << std::hex << number << std::dec << ' ' << *reinterpret_cast<double*>(&number) << '\n'; //? 1
+//?     // clear the old exponent sign bit
+//?     number = number & (~MU_FLOAT_EXPONENT_SIGN_MASK);
+//?     cerr << "4: " << std::hex << number << std::dec << ' ' << *reinterpret_cast<double*>(&number) << '\n';
+  }
+  double result = *reinterpret_cast<double*>(&number);
+  return result;
+}
+
+void test_integer_representation() {
+  // Assuming long long int is 8 bytes:
+  static const long long int nbits = 64;
+//?   cerr << '\n'; //? 1
+//?   cerr << nbits << " iterations\n"; //? 1
+//?   cerr << std::hex; //? 1
+//?   cerr << "type mask: " << MU_NUMBER_TYPE_MASK << '\n'; //? 1
+//?   cerr << "sign mask: " << MU_NUMBER_SIGN_MASK << '\n'; //? 1
+//?   cerr << std::dec; //? 1
+  // until the last 2 bits all integers retain their value
+  for (int i = 0; i < nbits-2; ++i) {
+    long long int n = (0x1LL << i);
+//?     cerr << i << ": " << "0x" << std::hex << n << std::dec << ' ' << n << " => " << to_integer(n) << '\n'; //? 1
+    CHECK(is_integer(n));
+    CHECK(n == to_integer(n));
+  }
+  long long int n = (0x1LL << (nbits-2));
+  CHECK(is_integer(n));
+//?   cerr << nbits-2 << ": " << "0x" << std::hex << n << std::dec << ' ' << n << " => " << to_integer(n) << '\n'; //? 1
+  CHECK(is_negative(n));
+}
+
+void test_noninteger_representation() {
+  // Assuming long long int is 8 bytes:
+  static const long long int nbits = 64;
+  static const long long int FLOAT_MASK = (0x1LL << (nbits-1));
+  double f = -2.0;
+  printf("0x%llx\n", *(long long int*)&f);
+  cout << '\n';
+  for (int fraction = 0; fraction < 52; ++fraction) {
+    for (int exponent = 52; exponent < 63; ++exponent) {
+      long long int n = (0x1LL << fraction) | (0x1LL << exponent) | FLOAT_MASK;
+      CHECK(is_float(n));
+      double result_on_host = *reinterpret_cast<double*>(&n);
+      double result = to_float(n);
+      printf("%02d %d: 0x%llx %.30e\n", fraction, exponent, n, result_on_host);
+      printf("=>                        %.30e\n", result);
+    }
+  }
+//?   long long int n = ((0x1LL << (nbits-2)) | FLOAT_MASK);
+//?   CHECK(is_float(n));
+//?   double result_on_host = *reinterpret_cast<double*>(&n);
+//?   double result = to_float(n);
+//?   cout << nbits-2 << ": " << "0x" << std::hex << n << std::dec << ' ' << result_on_host
+//?        << " => " << result << '\n';
+}
+
+:(before "End Includes")
+#include<iomanip>
+#include<limits>