c++ how to get “one digit exponent” with printf

后端 未结 5 2134
说谎
说谎 2020-12-06 05:48

Is there a way to print in scientific notation less than 3 places for exponent part of number? The 6.1 formatting doesn\'t affect exponent but only the number part:

相关标签:
5条回答
  • 2020-12-06 06:04

    With standard C printf() this can't be done (and the use of three digits by default seems wrong as well), at least in C99 (I don't have a newer version at hand). The relevant quote from the C99 standard is at 7.19.6.1 paragraph 8, formats e,f:

    .... The exponent always contains at least two digits, and only as many more digits as necessary to represent the exponent. If the value is zero, the exponent is zero. ...

    The best bet to fit this [portably] into code using lots of these outputs is to use C++ IOStreams: although the default formatting is the same as in C, it is possible to install a custom facet into the stream's std::locale which does the formatting the way you need. That said, writing the formatting code might not be entirely trivial. Although I would probably just built on the standard conversion and then remove the excess zeros after the e character.

    0 讨论(0)
  • 2020-12-06 06:07

    C/C++ specifies at least two exponent digits with printf("%e",...). To print only 1, and to deal with Visual Studio which, by default, prints at least 3, additional code is needed.

    Consider IOStreams @Dietmar Kühl

    If C++ code still wants to use printf() style formats:

    Adjusting the value of a double before calling printf() too often results in rounding issues, range shorting and general corner case failures like dealing with log10(0.0). Also consider large double just near a power-of-10 where log10() may come up short, -0.0, INF, NAN.

    In this case, better to post-process the string.

      double var = 1.23e-9;
      //                    - 1 . x e - EEEEE \0
      #define ExpectedSize (1+1+1+1+1+1+  5 + 1)
      char buf[ExpectedSize + 10];
      snprintf(buf, sizeof buf, "%.1e", var);
      char *e = strchr(buf, 'e');  // lucky 'e' not in "Infinity" nor "NaN"
      if (e) {
        e++;
        int expo = atoi(e);
        snprintf(e, sizeof buf - (e - buf), "%1d", expo);
      }
      printf("'%6s'\n", buf);  // '1.2e-9'
    

    Note: %e is amiable to post-processing as its width is not so unwieldy as "%f". sprintf(buf, "%f", DBL_MAX) could be 1000s of char.

    0 讨论(0)
  • 2020-12-06 06:16

    I found Zach's answer to be the fastest and simplest method and is also applicable to any OS. I did find that two modifications were needed on the "base =" line for it to work for all numbers. (Otherwise nan's when exponent is negative in cygwin). The extra print statement is just for patran neutral file compatibility. I would have upvoted his answer, but I just started on stackexchange so I don't have sufficient "reputation".

    void PrintScientific(double d)
    {
       int exponent  = (int)floor(log10( fabs(d)));  // This will round down the exponent
       double base      = (d * pow(10.0,  -1*exponent));
    
    if(abs(exponent)<10)
        printf("%13.9lfE%+01d", base, exponent);
    else
        printf("%12.9lfE%+01d", base, exponent);
    }
    
    0 讨论(0)
  • 2020-12-06 06:19

    According to Wikipedia:

    The exponent always contains at least two digits; if the value is zero, the exponent is 00. In Windows, the exponent contains three digits by default, e.g. 1.5e002, but this can be altered by Microsoft-specific _set_output_format function.

    _set_output_format

    0 讨论(0)
  • 2020-12-06 06:21

    I've had to do this a lot (I write file parsers and some file formats like NITF require you to store numeric values as strings).

    What you do is an exploit based on what base-10 math (scientific notation) really means: It means that for all real numbers y, y = (x) * 10^(N) for some integer N and some x in the range (-1, 1) exclusive.

    So, you do the following

    void PrintScientific(double d)
    {
       int exponent  = (int)floor(log10( fabs(d)));  // This will round down the exponent
       double base   = d * pow(10, -1.0*exponent);
    
       printf("%lfE%+01d", base, exponent);
    }
    

    You can add all the format specifiers you need to control the # of chars before, after the "." decimal place.

    Do NOT forget the rounding step! This is how it works, using the properties of base10 and logarithms (base 10 here):

    Let y = x * 10^N =>
    log(y) = log(x*10^N) =>
    log(y) = log(x) + log(10^N) => // From Log "product" rule
    log(y) = log(x) + N

    Since x is in the range (-10, 10) -"()" means exclusive(exclusive), that implies log(x) is in the range (-1, 1). So when we round down for integer conversion, we're dropping "log(x)" contribution. You can then get the "x" portion from the original number, which lets you output the original in any scientific notation you want to use.

    0 讨论(0)
提交回复
热议问题