wolfgangj / bone-lisp

The Bone Lisp programming language
Other
321 stars 21 forks source link

Extend integers range to -2^59..2^59-1 #10

Closed dubek closed 8 years ago

dubek commented 8 years ago

The 3 least significant bits (of an any variable) are used for the t_num tag; 1 bit (zero) reserved; the 60 most significant bits are used for the raw signed integer.

Added tests for the integer range and for num->str (reflecting the new range).

wolfgangj commented 8 years ago

What do you think about reducing it from 61 to 60 bits so that we have one bit left to also store floats in t_nums later?

wolfgangj commented 8 years ago

I vaguely remember that bit-shifting will not preserve the sign on all plattforms, I'll look up what's up with that. If this is indeed an issue, I have code to handle this laying around somewhere.

dubek commented 8 years ago

Re: floats: So you mean something like:

+--------------+------------------+--------------------+ 
| 60-bit value | is_float (1 bit) | t_num tag (3 bits) |
+--------------+------------------+--------------------+

BTW if we store a float (32-bit) it's easy (just store them in the upper 32-bit word, waste the other 28 bits). If we want higher precision float (=double) - how do you "truncate" a double (64-bit) to 60 bits?

Re: bit-shift: I think that in the C standard a bit-shift of a signed integer should preserve the sign. The C compiler should translate that to the proper assembly instructions for the target platform.

wolfgangj commented 8 years ago

Re floats: Yes, that was the plan. I don't want to mess with floating point representations, so we'd be limited to 32 bit floats. If that's not enough, we could use an additional non-immediate data type.

dubek commented 8 years ago

OK, to summarize:


Integers:

+------------------------------+-----------+--------------------+
| 60-bit signed integer        | 0 (1 bit) | t_num tag (3 bits) |
+------------------------------+-----------+--------------------+

Floats:

+--------------+---------------+-----------+--------------------+
| 32-bit float | 28-bit unsued | 1 (1 bit) | t_num tag (3 bits) |
+--------------+---------------+-----------+--------------------+

I'll modify this PR to implement the integers as above. Floats - later...

wolfgangj commented 8 years ago

Great, thanks.

wolfgangj commented 8 years ago

Re bit-shifting: Right-shifting a negative value is implementation-defined. Left-shifting that moves a bit into the sign (or past it) is undefined. So the current approach is not portable. :-/

wolfgangj commented 8 years ago

This is the code I wrote for an earlier project, we can reuse parts of it (a bit of explanation is below).

static always_inline pure any
sint (integer_t i)
{
  if (!(i >= SINT_MIN && i <= SINT_MAX))
    err ("number cannot be represented as sint");

  return mk ((i << 1) | 1); /* ...1 */
}

static always_inline pure integer_t
unsint (any si)
{
  if (!sint_p (si))
    err ("sint expected");
#if (-1 >> 1) == -1 /* Does bit shifting preserve the sign? */
  return ((intptr_t) id (si)) >> 1;
#else
  return ((((intptr_t) id (si)) < 0)
      ? (~((~((intptr_t) id (si))) >> 1)) /* Negate before and after */
      : (((intptr_t) id (si)) >> 1));
#endif
}
dubek commented 8 years ago

My last commit 9cb1bca6095d9e46d3a13c91b5e91a3e0f3c993c reserves one bit for numeric type (0=integer), and uses the correct right-shifting copied from your code above.

dubek commented 8 years ago

Added out of range errors in 4ca1a84fd7e166c38b5b1080f74059ec4d4d842d .

wolfgangj commented 8 years ago

Thanks, I have merged it now. But don't be surprised if I modify a few details of it later. For example, by using the new type type_num_tag we abstract the bit value, but not its position; it might be slightly more elegant to do both, so I might change that together with some things I didn't do well in the past.