Closed dubek closed 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_num
s later?
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.
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.
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.
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...
Great, thanks.
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. :-/
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
}
sint
means "small/signed integer"id()
was used here to extract the value from an any
(which was defined as a union).My last commit 9cb1bca6095d9e46d3a13c91b5e91a3e0f3c993c reserves one bit for numeric type (0=integer), and uses the correct right-shifting copied from your code above.
Added out of range errors in 4ca1a84fd7e166c38b5b1080f74059ec4d4d842d .
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.
The 3 least significant bits (of an
any
variable) are used for thet_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).