用sprintf科学符号打印所有有效数字

时间:2022-05-22 14:57:29

When R converts a large number to a string in scientific notation, it includes all significant digits and no trailing zero's. Is it possible to accomplish this in C with sprintf?

当R用科学的符号把一个大数字转换成一个字符串时,它包含了所有的有效数字,没有后面的零。是否有可能用C语言和sprintf实现这一点?

> as.character(12345e11)
[1] "1.2345e+15"
> as.character(1234500000e6)
[1] "1.2345e+15"
> as.character(1234500001e6)
[1] "1.234500001e+15"

I tried sprintf(buf, "%g", val) but this seems to include 5 decimal digits at most. I also tried to set a higher precision with sprintf(buf, "%.18g", val) but this will include non significant digits and trailing zeros.

我尝试过sprintf(buf,“%g”,val),但这似乎最多包含5位小数。我还尝试用sprintf(buf, %)来设置更高的精度。但是这将包括不重要的数字和后面的0。

Is there a way to get the behavior of sprintf(buf, "%g", val) but increase the 5 digit limit?

是否有一种方法可以使sprintf(buf,“%g”,val)的行为增加5位数的限制?

1 个解决方案

#1


1  

Code could use "%.18e" or "%.18g", but the question is how large should "18" be? Is 18 the best value? The answer lies in DBL_DECIMAL_DIG.

代码可以使用“%。18 e”或“%。18克,但问题是“18”应该有多大?18是最好的值吗?答案就在DBL_DECIMAL_DIG中。

DBL_DECIMAL_DIG is the minimum number of significant digits to print to insure the round-trip of double to string to the same exact double for all double.

DBL_DECIMAL_DIG是用于打印的有效数字的最小数目,以确保所有双精度双精度双精度双精度双精度双精度双精度。

Recommend using format specifier "%.*e".

建议使用格式说明符“%.*e”。

Note that the "18" in "%.18e" is the number of significant digits after the decimal point. So "%.18e" prints 19 significant digits.

注意“%”中的“18”。18e是小数点后有效位数。所以“%。18e打印出19位有效数字。


Use printf("%a", x); which prints in a hexadecimal output.
For a decimal output:

使用printf(" % ",x);在十六进制输出中打印。对于一个十进制输出:

#include <float.h>

//    sign + digit +  dp +       digits          + e + sign + expo + \0
char buf[1 + 1 +      1  + (DBL_DECIMAL_DIG - 1) + 1 + 1    + 5    + 1]; 
sprintf(buf, "%.*e", DBL_DECIMAL_DIG - 1, x);

Ref Printf width specificer to maintain precision of floating-point value

Ref Printf宽度指定器以保持浮点值的精度


A number like y = 1.0/3.0 using the typical double binary64 format would need to see about 53 decimal digits to see its exact value. But many of the trailing digits are not needed for a successful round-trip.

使用典型的双二进制64格式的y = 1.0/3.0这样的数字需要看到大约53位小数才能看到它的确切值。但是,许多尾随数字并不需要成功的往返。


Now we know the most digits to print, use the below to get rid of those pesky trailing 0 digits.

现在我们知道要打印的数字最多了,用下面的数字去掉那些讨厌的0后面的数字。

#include <float.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

char *trim0(double x, char *buf) {
  sprintf(buf, "% .*e", DBL_DECIMAL_DIG - 1, x);
  if (isfinite(x)) {
    char *p = &buf[DBL_DECIMAL_DIG + 1];  // address of last significand digit
    char *t;
    for (t=p; *t == '0'; t--);
    memmove(t+1, p+1, strlen(p+1)+1);
  }
  return buf;
}

int main(void) {
  char buf[1 + 1 + 1 + (DBL_DECIMAL_DIG - 1) + 1 + 1 + 5 + 1];
  printf("%s\n", trim0(1.2, buf));
  printf("%s\n", trim0(1.0/7, buf));
  return 0;
}

Output

输出

 1.2e+00
 1.4285714285714285e-01

#1


1  

Code could use "%.18e" or "%.18g", but the question is how large should "18" be? Is 18 the best value? The answer lies in DBL_DECIMAL_DIG.

代码可以使用“%。18 e”或“%。18克,但问题是“18”应该有多大?18是最好的值吗?答案就在DBL_DECIMAL_DIG中。

DBL_DECIMAL_DIG is the minimum number of significant digits to print to insure the round-trip of double to string to the same exact double for all double.

DBL_DECIMAL_DIG是用于打印的有效数字的最小数目,以确保所有双精度双精度双精度双精度双精度双精度双精度。

Recommend using format specifier "%.*e".

建议使用格式说明符“%.*e”。

Note that the "18" in "%.18e" is the number of significant digits after the decimal point. So "%.18e" prints 19 significant digits.

注意“%”中的“18”。18e是小数点后有效位数。所以“%。18e打印出19位有效数字。


Use printf("%a", x); which prints in a hexadecimal output.
For a decimal output:

使用printf(" % ",x);在十六进制输出中打印。对于一个十进制输出:

#include <float.h>

//    sign + digit +  dp +       digits          + e + sign + expo + \0
char buf[1 + 1 +      1  + (DBL_DECIMAL_DIG - 1) + 1 + 1    + 5    + 1]; 
sprintf(buf, "%.*e", DBL_DECIMAL_DIG - 1, x);

Ref Printf width specificer to maintain precision of floating-point value

Ref Printf宽度指定器以保持浮点值的精度


A number like y = 1.0/3.0 using the typical double binary64 format would need to see about 53 decimal digits to see its exact value. But many of the trailing digits are not needed for a successful round-trip.

使用典型的双二进制64格式的y = 1.0/3.0这样的数字需要看到大约53位小数才能看到它的确切值。但是,许多尾随数字并不需要成功的往返。


Now we know the most digits to print, use the below to get rid of those pesky trailing 0 digits.

现在我们知道要打印的数字最多了,用下面的数字去掉那些讨厌的0后面的数字。

#include <float.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

char *trim0(double x, char *buf) {
  sprintf(buf, "% .*e", DBL_DECIMAL_DIG - 1, x);
  if (isfinite(x)) {
    char *p = &buf[DBL_DECIMAL_DIG + 1];  // address of last significand digit
    char *t;
    for (t=p; *t == '0'; t--);
    memmove(t+1, p+1, strlen(p+1)+1);
  }
  return buf;
}

int main(void) {
  char buf[1 + 1 + 1 + (DBL_DECIMAL_DIG - 1) + 1 + 1 + 5 + 1];
  printf("%s\n", trim0(1.2, buf));
  printf("%s\n", trim0(1.0/7, buf));
  return 0;
}

Output

输出

 1.2e+00
 1.4285714285714285e-01