Writing IEEE 754-1985 as ASCII in a 16-byte Limited String

This is a continuation of my original post . But I will repeat this for clarity:

According to the DICOM standard, a floating point type can be stored using a representation of decimal string values. See Table 6.2-1. Representations of DICOM values :

Decimal string: A character string representing either a fixed point number or a floating point number. The fixed-point number must contain only characters 0–9 with the optional leading character “+” or “-,” and optional “.”. to mark the decimal point. Floating-point numbers must be transmitted, as defined in ANSI X3.9, from "E" or "e" to indicate the beginning of the exponent. Decimal lines can be padded with leading or trailing spaces. Nested spaces are not allowed.

"0" - "9", "+", "-", "E", "e", "." and the default SPACE value. Character repertoire. 16 bytes maximum

The standard says that the textual representation is a fixed point or a floating point. The standard only refers to how values ​​are represented inside the DICOM dataset itself. Thus, there is no need to load a fixed point text view into a fixed point variable.

So, it is now clear that the DICOM standard explicitly recommends double(IEEE 754-1985) for representing a Value Representationtype Decimal String(maximum 16 significant digits). My question is, how can I use the standard C I / O library to convert this binary representation from memory in ASCII to this limited string?

From a random source on the Internet, this is a non-trivial but commonly accepted solution :

printf("%1.16e\n", d); // Round-trippable double, always with an exponent

or

printf("%.17g\n", d); // Round-trippable double, shortest possible

, , , 16 . , 16 ?


: , . hex/uuencode.

2. , travis-ci:

:

, :

  • compute1.c : 0.0095729050923877828
  • compute2.c : 0.21764383725715469
  • compute3.c : 4.050031792674619
  • compute4.c : 0.001287056579548422

So compute4.c (0.001287056579548422 < 4,050031792674619), (x3) ( time).

+4
5

, .

, , .

  • , 1 - '-'.

  • '+' 'e'.

  • '.' .

  • - sprintf() , . , FLT_EVAL_METHOD .., .

  • 1 , . . 14 20, 13 12, 11.

  • - '.' sprintf() 1) 2) a double .

  • 1 2 000 000 000, -1.00000000049999e-200. 1 50 000 000 000.

  • 14- , , , 12345678901234e1, 16-2 .


static size_t shrink(char *fp_buffer) {
  int lead, expo;
  long long mant;
  int n0, n1;
  int n = sscanf(fp_buffer, "%d.%n%lld%ne%d", &lead, &n0, &mant, &n1, &expo);
  assert(n == 3);
  return sprintf(fp_buffer, "%d%0*llde%d", lead, n1 - n0, mant,
          expo - (n1 - n0));
}

int x16printf(char *dest, size_t width, double value) {
  if (!isfinite(value)) return 1;

  if (width < 5) return 2;
  if (signbit(value)) {
    value = -value;
    strcpy(dest++, "-");
    width--;
  }
  int precision = width - 2;
  while (precision > 0) {
    char buffer[width + 10];
    // %.*e prints 1 digit, '.' and then `precision - 1` digits
    snprintf(buffer, sizeof buffer, "%.*e", precision - 1, value);
    size_t n = shrink(buffer);
    if (n <= width) {
      strcpy(dest, buffer);
      return 0;
    }
    if (n > width + 1) precision -= n - width - 1;
    else precision--;
  }
  return 3;
}

double rand_double(void) {
  union {
    double d;
    unsigned char uc[sizeof(double)];
  } u;
  do {
    for (size_t i = 0; i < sizeof(double); i++) {
      u.uc[i] = rand();
    }
  } while (!isfinite(u.d));
  return u.d;
}

void x16printf_test(double value) {
  printf("%-27.*e", 17, value);
  char buf[16+1];
  buf[0] = 0;
  int y = x16printf(buf, sizeof buf - 1, value);
  printf(" %d\n", y);
  printf("'%s'\n", buf);
}


int main(void) {
  for (int i = 0; i < 10; i++)
    x16printf_test(rand_double());
}

-1.55736829786841915e+118   0
'-15573682979e108'
-3.06117209691283956e+125   0
'-30611720969e115'
8.05005611774356367e+175    0
'805005611774e164'
-1.06083057094522472e+132   0
'-10608305709e122'
3.39265065244054607e-209    0
'33926506524e-219'
-2.36818580315246204e-244   0
'-2368185803e-253'
7.91188576978592497e+301    0
'791188576979e290'
-1.40513111051994779e-53    0
'-14051311105e-63'
-1.37897140950449389e-14    0
'-13789714095e-24'
-2.15869805640288206e+125   0
'-21586980564e115'
+2

formatter . , %g (e20 e+020: 2 ), :

  • %.17g
  • 16 , , 16
  • .

:

void encode(double f, char *buf) {
    char line[40];
    char format[8];
    int prec;
    int l;

    l = sprintf(line, "%.17g", f);
    if (l > 16) {
        prec = 33 - strlen(line);
        l = sprintf(line, "%.*g", prec, f);
        while(l > 16) {
            /* putc('.', stdout);*/
            prec -=1;
            l = sprintf(line, "%.*g", prec, f);
        }
    }
    strcpy(buf, line);
}

( e30 e + 030), % 1.16e . ( ):

  • %1.16e ( 10)
  • -2 () ():
  • 0 -2 ( ): .
  • -1 -3 ( ): , 0
  • else e

:

  • , - -1
  • : >=5, , 9. 9.9999999999... , 10

:

void clean(char *mant) {
    char *ix = mant + strlen(mant) - 1;
    while(('0' == *ix) && (ix > mant)) {
        *ix-- = '\0';
    }
    if ('.' == *ix) {
        *ix = '\0';
    }
}

int add1(char *buf, int n) {
    if (n < 0) return 1;
    if (buf[n] == '9') {
        buf[n] = '0';
        return add1(buf, n-1);
    }
    else {
        buf[n] += 1;
    }
    return 0;
}

int doround(char *buf, unsigned int n) {
    char c;
    if (n >= strlen(buf)) return 0;
    c = buf[n];
    buf[n] = 0;
    if ((c >= '5') && (c <= '9')) return add1(buf, n-1);
    return 0;
}

int roundat(char *buf, unsigned int i, int iexp) {
    if (doround(buf, i) != 0) {
        iexp += 1;
        switch(iexp) {
            case -2:
                strcpy(buf, ".01");
                break;
            case -1:
                strcpy(buf, ".1");
                break;
            case 0:
                strcpy(buf, "1.");
                break;
            case 1:
                strcpy(buf, "10");
                break;
            case 2:
                strcpy(buf, "100");
                break;
            default:
                sprintf(buf, "1e%d", iexp);
        }
        return 1;
    }
    return 0;
}

void encode(double f, char *buf, int size) {
    char line[40];
    char *mant = line + 1;
    int iexp, lexp, i;
    char exp[6];

    if (f < 0) {
        f = -f;
        size -= 1;
        *buf++ = '-';
    }
    sprintf(line, "%1.16e", f);
    if (line[0] == '-') {
        f = -f;
    size -= 1;
    *buf++ = '-';
    sprintf(line, "%1.16e", f);
    }
    *mant = line[0];
    i = strcspn(mant, "eE");
    mant[i] = '\0';
    iexp = strtol(mant + i + 1, NULL, 10);
    lexp = sprintf(exp, "e%d", iexp);
    if ((iexp >= size) || (iexp < -3)) {
        i = roundat(mant, size - 1 -lexp, iexp);
        if(i == 1) {
            strcpy(buf, mant);
            return;
        }
        buf[0] = mant[0];
        buf[1] = '.';
        strncpy(buf + i + 2, mant + 1, size - 2 - lexp);
        buf[size-lexp] = 0;
        clean(buf);
        strcat(buf, exp);
    }
    else if (iexp >= size - 2) {
        roundat(mant, iexp + 1, iexp);
        strcpy(buf, mant);
    }
    else if (iexp >= 0) {
        i = roundat(mant, size - 1, iexp);
        if (i == 1) {
            strcpy(buf, mant);
            return;
        }
        strncpy(buf, mant, iexp + 1);
        buf[iexp + 1] = '.';
        strncpy(buf + iexp + 2, mant + iexp + 1, size - iexp - 1);
        buf[size] = 0;
        clean(buf);
    }
    else {
        int j;
        i = roundat(mant, size + 1 + iexp, iexp);
        if (i == 1) {
            strcpy(buf, mant);
            return;
        }
        buf[0] = '.';
        for(j=0; j< -1 - iexp; j++) {
            buf[j+1] = '0';
        }
        if ((i == 1) && (iexp != -1)) {
            buf[-iexp] = '1';
            buf++;
        }
        strncpy(buf - iexp, mant, size + 1 + iexp);
        buf[size] = 0;
        clean(buf);
    }
}
+2

printf() "%e" " ... " E "" e ", "

[−]d.ddd...ddde±dd

-0.0. 2 .

DBL_MAX < 1e1000, ( IEEE 754-1985 double), : 1 , 1 , '.', 8 , 'e', , 3 .

(: " 16 ", , C. 1.)

// Room for 16 printable characters.
char buf[16+1];
int n = snprintf(buf, sizeof buf, "%.*e", 8, x);
assert(n >= 0 && n < sizeof buf);
puts(buf);

2 3 .

- - , 2 3 . , -0.0.

[Edit] .

:

// Room for 16 printable characters.
char buf[16+1];
assert(isfinite(x)); // for now, only address finite numbers

int precision = 8+1+1;
if (signbit(x)) precision--;  // Or simply `if (x <= 0.0) precision--;`
if (fabs(x) >= 9.99999999e99) precision--; // some refinement possible here.
else if (fabs(x) <= 1.0e-99) precision--;
int n = snprintf(buf, sizeof buf, "%.*e", precision, x);
assert(n >= 0 && n < sizeof buf);
puts(buf);

:

3 .
IEEE 754-1985 double , , , 15-17.

2:

// Room for N printable characters.
#define N 16
char buf[N+1];
assert(isfinite(x)); // for now, only address finite numbers

int precision = N - 2 - 4;  // 1.xxxxxxxxxxe-dd
if (signbit(x)) precision--;
int n = snprintf(buf, sizeof buf, "%.*e", precision, x);
if (n >= sizeof buf) {
  n = snprintf(buf, sizeof buf, "%.*e", precision - (n - sizeof buf) - 1, x);
}
assert(n >= 0 && n < sizeof buf);
puts(buf);
+2

, - printf ( "%. 17g\n", d); , . - , . , , .

, , n- 0.0 1.0, "49" 0.49. 0,5, . 0,50, . , , . , , .

, , - , 9 . , . (+ 9.99999e17) + 1e18, , .

, , sign/mantissa , , .

+1

, 17- , . , 16 , .

, . 16 , , .

If you want to print the results using less than 16 bytes, you can basically execute uuencode. That is, use more than 16 digits so you can compress more bits into each digit. If you use 64 different characters (six bits), then a 64-bit double can be printed with eleven characters. Not very readable, but compromises must be made.

0
source

Source: https://habr.com/ru/post/1607810/


All Articles