是什么原因" text-float-text"保证6位但是" float-text-float" 9?

时间:2017-11-05 15:49:43

标签: c++ floating-point

我正在阅读this,但实际上我无法理解为text-float-text保证6位数,而float-text-float应该为9(考虑single precision

text-float-text存储转换为正确的精度浮点数。只有在打印时才会出现'#34; round"版。但它是一台打印机"故障。

Code

int main()
{  
    float decimalFloat = 8.589973e9;    
    char const *decimalString = "8.589973e9";
    float const floatFromDecimalString = strtof(decimalString, nullptr);
    std::cout << decimalString << std::endl << std::scientific << floatFromDecimalString << std::endl;
    std::cout << "text-float-text: 6 digit preserved, not 7" << std::endl << std::endl;

    std::cout << "but the value is correctly converted..." << std::endl;
    std::cout << std::bitset<sizeof decimalFloat*8>(*(long unsigned int*)(&decimalFloat)) << std::endl;
    std::cout << std::bitset<sizeof floatFromDecimalString*8>(*(long unsigned int*)(&floatFromDecimalString)) << std::endl;        
} 

保留二进制文件。它在直接声明地板之间或在转换之后从相同的十进制数字存储为字符串:

01010000000000000000000000100110

为什么我们需要digits10?保留的数字是max_digits10。如果打印轮次严重&#34;,那么......这似乎是打印机的问题。

应该知道实际的浮动值有效数字为max_digits10而不是digits10(即使您在打印时查看数字10)。

3 个答案:

答案 0 :(得分:2)

有时,一些显示计数器示例的代码会有所帮助。 这是C代码,但{+ 1}}特性在C ++ / C中是相同的。

  

“text-float-text”保证6位数的原因是什么。

7位十进制数字不往返。考虑像float这样的文字。该值转换为"9999999e3"。然而,只有一个有效的24位有效二进制数字,下一个float是1024。由于该区域中的后续文字值为float或1,000,因此最终附近的文字值会转换为相同的1e3

6个十进制数字始终有效,因为后续文本值中的步长始终小于二进制数字中的步长。

float

输出

void text_to_float_test(void) {
  unsigned long ten = 10*1000*1000;
  float f1,f2;
  for (unsigned long i = ten; i>0; i--) {
    char s1[40];
    sprintf(s1+0, "%lue3", i);
    sscanf(s1, "%f", &f1);
    char s2[40];
    sprintf(s2 + 0, "%lue3", i-1);
    sscanf(s2, "%f", &f2);
    if (f1 == f2) {
      printf("\"%s\" and \"%s\" both convert to %.*e\n", s1, s2, 7-1, f1);
      return;
    }
  }
  puts("Done");
}
  

但“float-text-float”有9?

在每对2次幂之间,通常有2个 23 不同"9999979e3" and "9999978e3" both convert to 9.999978e+09 。当仅使用8个有效十进制数字时,FP值float和下一个1.000000954e+01 float都转换为相同的文本。

更深:在8到16之间,由于binary encoding of FP numbers,有2个 23 不同1.000001049e+01线性分布。其中1/8在10到11或1,048,576之间。仅使用float仅会产生1,000,000个不同的文本。需要更多的十进制数字。

10.xxx xxxx

输出

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

int float_to_text(float x0, float x1, int significant_digits) {
  char s0[100];
  char sn[100];
  while (x0 <= x1) {
    sprintf(s0, "%.*e", significant_digits-1, x0);
    float xn = nextafterf(x0, x0*2);  // next higher float
    sprintf(sn, "%.*e", significant_digits-1, xn);
    if (strcmp(s0,sn) == 0) {
      printf("%2d significant_digits: %.12e and the next float %.12e both are \"%s\"\n", 
          significant_digits, x0, xn, s0);
      fflush(stdout);
      return 1;
    }
    x0 = xn;
  }
  return 0;
}

void float_to_text_test(float x0) {
  int significant_digits = 5;
  while (float_to_text(x0, x0*2, significant_digits)) {
    significant_digits++;
  }
  printf("%2d significant digits needed %.*e to %.*e\n", //
      significant_digits, significant_digits, x0, significant_digits, x0*2);
}

int main(void) {
  float_to_text_test(8.0);
}

答案 1 :(得分:2)

十进制→二进制→十进制

考虑七位十进制浮点值9,999,979•10 3 (9,999,979,000)和9,999,978•10 3 (9,999,978,000)。当您将这些转换为具有24位有效位数的二进制浮点数时,在这两种情况下都会得到1001 0101 0000 0010 1110 0100•2 10 (9,999,978,496),因为这是最接近的二进制浮点值每个数字。 (下一个较低和较高的二进制浮点数是1001 0101 0000 0010 1110 0011•2 10 (9,999,977,472)和1001 0101 0000 0010 1110 0101•2 10 (9,999,979,520 )。)

因此,24位有效数字不能用七位有效数字区分所有十进制浮点数。我们最多可以做六位数。

二进制→十进制→二进制

考虑两个24位有效二进制浮点值1111 1111 1111 1111 1111 1101•2 3 (134,217,704)和1111 1111 1111 1111 1111 1100•2 3 (134,217,696)。如果将这些转换为带有八位有效数字的十进制浮点数,则在两种情况下都会得到13,421,770•10 1 。然后你不能分开它们。所以你需要至少九位小数。

你可以把这看作是数字位置所在的一些“分块”。在十进制数字的顶部,我们需要一个足够大,在第一个数字中超过5。但是,最接近的2的幂不一定以5的那个位置开始 - 它可能以6,或7,或8或9开始,因此存在一些浪费。在底部,我们需要在最后一位数字中略低于1。但是,最接近的2的幂不一定从下一个较低位置的9开始。它可能从8或7或6或甚至5开始。再次,有一些浪费。要从二进制转换为十进制到二进制,您需要足够的十进制数来使绕>浪费,因此您需要额外的十进制数字。要从十进制到二进制到十进制,你必须保持十进制数字足够少,以便它们加上浪费符合里面二进制数,所以你需要更少的十进制数字。

答案 2 :(得分:2)

  

“text-float-text”保证6位数但“float-text-float”能保证9位的原因是什么?

  1. 从二进制FP的角度来看,前导十进制数字1到9包含不同的信息量:1到3+位。

  2. float0.1250.52.0 1.6等)和十进制文字(“0.001”的绝对精度变化不同“,”0.1“,”10.0“,”10000.0“等。

  3. 这两个导致摆动精度的效果。

    要看到这一点,让我们使用pigeon hole principle

    text 具有n个有效十进制数字,其形式为
    -1 sign ×1_to_9。(n-1)decimal_digits×10 exponent

    C ++通常将float编码为binary32。大多数值的形式如下:
    -1 sign ×1.23_bit_fraction×2 指数 - 偏移量

    文本 - float - 文本

    考虑一个“最坏情况”的情况,其中 text 包含大量信息 - 其最重要的数字接近9比1。

    在[1.0e9 ... 10.0e9]范围内,并使用7位有效十进制数字,文本值间隔1000。

    在选择范围[2 33 ... 2 34 )或[8.589934592e9 ... 17.179869184e9)中,有2 23 不同float线性间隔1024。

    9.999872000e9和
    9.999744000e9可以精确编码为float和7位十进制文本。差异是
    0.000128000e9或128,000。

    它们之间是127个不同的7位十进制文本值和124个不同的float。如果代码尝试将所有127个文本值编码为float并返回相同的文本,则只会成功124次。

    示例:“9.999851e9”和“9.999850e9”都转换为float 9.999850496000e+09

    相反,如果文本值仅为 6 有效十进制数字,则往返始终有效。

    float - 文本 - float

    考虑一个“最坏情况”的情况,其中 text 包含的信息很少 - 其最重要的数字接近1而不是9。

    在[8.0 ... 16.0]范围内,有2个 23 或8,388,608个不同的float线性间隔。

    在[10.0 ... 11.0]范围内,有1/8×2 23 或1,048,576个不同的float值。

    在[10.000000 ... 11.000000)范围内,并使用8个有效小数位数,有1,000,000个不同的 text 值。

    如果代码尝试将所有1,048,576个float值编码为只有8个十进制数字的文本,然后再返回到相同的float,则它只会成功1,000,000次。

    需要

    9 十进制数字。