Last active
June 17, 2018 22:12
-
-
Save jido/b146f66d0b64bc2d2f640cde22f28abc to your computer and use it in GitHub Desktop.
Decimal number format that makes some sense
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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