Skip to content

Instantly share code, notes, and snippets.

@jido
Last active June 17, 2018 22:12
Show Gist options
  • Select an option

  • Save jido/b146f66d0b64bc2d2f640cde22f28abc to your computer and use it in GitHub Desktop.

Select an option

Save jido/b146f66d0b64bc2d2f640cde22f28abc to your computer and use it in GitHub Desktop.
Decimal number format that makes some sense
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * numberAsString(uint64_t num) {
static char result[] = "+1.234567890123456e-123";
char sign = (num & 0x8000000000000000L) ? '-' : '+';
uint16_t scale = (num >> 50) & 0x1fff;
if (scale << 2 == 0x7ff0)
{
static char inf[] = "+Infinity";
inf[0] = sign;
return inf;
}
uint32_t units = (scale != 0) * (1 + (scale - 1) % 9);
int exp = -455 + (scale != 0) * (scale - 1) / 9;
uint32_t s[5];
uint32_t shift = 50;
for (int i = 0; i < 5; ++i)
{
shift -= 10;
s[i] = (num >> shift) & 0x3ff;
if (s[i] >= 1000)
{
return "NaN";
}
}
sprintf(result, "%c%u.%.3u%.3u%.3u%.3u%.3ue%+d", sign, units, s[0], s[1], s[2], s[3], s[4], exp);
return result;
}
uint64_t makeNumber(int units, uint32_t s1, uint32_t s2, uint32_t s3, uint32_t s4, uint32_t s5, int exp) {
uint64_t sign = (units < 0);
uint64_t scale = (units != 0) * (abs(units) + 9 * (455 + exp));
return (sign << 63) | (scale << 50) | ((uint64_t)s1 << 40) | ((uint64_t)s2 << 30) | (s3 << 20) | (s4 << 10) | s5;
}
uint64_t shiftDecimals(uint64_t decimals, int amount) {
const int small = (amount + 810) % 3;
const int final_shift = (amount - small) * 10 / 3;
const uint16_t m[] = {1, 10, 100};
const uint32_t limit[] = {1000, 100, 10};
uint64_t raw = decimals * m[small];
uint64_t compens = 0;
for (uint32_t shift = 50; shift > 0;)
{
shift -= 10;
uint16_t s = ((decimals >> shift) & 0x3ff);
if (s >= limit[small])
{
compens |= (s * m[small] / 1000 * 24L) << shift; // | okay because 24 = 0x18 and 99 * 24 < (0x8 << 10)
}
}
raw += compens;
return (amount >= 0) ? (raw << final_shift) : (raw >> -final_shift);
}
int main(int n, char * args[]) {
puts(numberAsString(0x7fefe7f9fe7f9fe7L)); // Largest number 6.99...e+454
puts(numberAsString(0x0004000000000000L)); // Smallest normal number 1.0e-455
puts(numberAsString(0x0003e7f9fe7f9fe7L)); // Largest subnormal number 0.99...e-455
puts(numberAsString(0x0000000000000001L)); // Smallest number 1.0e-470
puts(numberAsString(makeNumber(+4, 698, 543, 100, 238, 333, +10)));
puts(numberAsString(0xfff)); // Not a number (invalid 3-decimal block)
puts(numberAsString(0x7ff0000000000000L)); // +∞
puts(numberAsString(makeNumber(-7, 0, 0, 0, 0, 0, +454))); // -∞
printf("Zero: 0x%.16llx \n", makeNumber(0, 0, 0, 0, 0, 0, 0)); // 0
printf("One: 0x%.16llx \n", makeNumber(+1, 0, 0, 0, 0, 0, 0)); // 0x4000000000000000
printf("Ten: 0x%.16llx \n", makeNumber(+1, 0, 0, 0, 0, 0, +1)); // 0x4024000000000000
printf("Minus five: 0x%.16llx \n", makeNumber(-5, 0, 0, 0, 0, 0, 0)); // 0xc010000000000000
uint64_t v = 0x001236f9c0cf9fddL;
uint64_t res = shiftDecimals(v, -5);
char converse[100] = {0};
strncpy(converse, numberAsString(v), 99); //shiftDecimals(res, +4)), 99);
printf("0x%.16llx (%s) * 1e-5 = 0x%.16llx (%s) \n", v, converse, res, numberAsString(res));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment