提问者:小点点

“文本-浮动-文本”保证6位但“浮动-文本-浮动”做9位的原因是什么?


我正在阅读这个,但我真的不明白为什么text-float-text保证6位数字,而float-text-flot应该是9(考虑到单精度)。

ext-float-text存储转换为正确精度的浮点数。仅当打印发生“四舍五入”版本时。但这是“打印机”故障。

代码:

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。如果打印“糟糕”,那么……这似乎是打印机的问题。

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


共3个答案

匿名用户

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

“文本-浮动-文本”保证6位数的原因是什么。

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

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

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");
}

输出

"9999979e3" and "9999978e3" both convert to 9.999978e+09

但是“浮子-文本-浮子”可以9吗?

在每个2的幂对之间,通常有2个不同的浮点数。当只使用8个有效十进制数字时,FP值1.000000954e 01和下一个flot1.000001049e 01都转换为相同的文本。

更深层次:在8到16之间,由于FP数字的二进制编码,有2个不同的浮点数线性分布。其中1/8在10到11或1,048,576之间。仅使用10.xxxxxx只能产生1,000,000种不同的文本。需要更多的十进制数字。

#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);
}

输出

 5 significant_digits: 8.000000000000e+00 and the next float 8.000000953674e+00 both are "8.0000e+00"
 6 significant_digits: 8.000000000000e+00 and the next float 8.000000953674e+00 both are "8.00000e+00"
 7 significant_digits: 8.000009536743e+00 and the next float 8.000010490417e+00 both are "8.000010e+00"
 8 significant_digits: 1.000000953674e+01 and the next float 1.000001049042e+01 both are "1.0000010e+01"
 9 significant digits needed 8.000000000e+00 to 1.600000000e+01

匿名用户

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

因此,24位有效数无法区分所有具有七位有效数的十进制浮点数。我们最多可以做到六位。

考虑两个24位有效的二进制浮点值1111 1111 1111 1111 1111 1101•23(134,217,704)和1111 1111 1111 1111 1111 1100•23(134,217,696)。如果您将它们转换为具有八位有效数的十进制浮点数,则在这两种情况下都将获得13,421,770•101。然后您无法区分它们。因此您至少需要九个十进制数字。

你可以认为这是数字位置所迫使的一些“分块”。在十进制数的顶部,我们需要足够大的数字来超过第一位的5。但是,最接近的2的幂不一定会以该位置的5开头——它可能以6、7、8或9开头,所以它有一些浪费。在底部,我们需要比最后一位的1低一点。但是最接近的2的幂不一定以下一个较低位置的9开头。它可能以8、7、6甚至5开头。同样,还有一些浪费。从二进制到十进制再到二进制,你需要足够的十进制数字来适应浪费,所以你需要额外的十进制数字。从十进制到二进制再到十进制,你必须保持十进制数字足够少,这样它们加上浪费就适合二进制,所以你需要更少的十进制数字。

匿名用户

“文本-浮动-文本”保证6位但“浮动-文本-浮动”做9位的原因是什么?

>

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

    其中flot0.1250.52.01.6等)和十进制文本("0.001"、"0.1"、"10.0"、"'1000.0"等)的绝对精度变化不同。

    正是这两者的作用导致了摆动精度。

    为了看到这一点,让我们使用鸽子洞原理。

    对于具有n有效十进制数字的文本,它的形式为
    -1符号×1_to_9。(n-1)decimal_digits×10指数

    C通常将flot编码为binary32。大多数值的形式为:
    -1sign×1。23_bit_fraction×2指数-偏移

    文本-浮点-文本

    考虑一个“最坏情况”条件,其中文本包含大量信息——它的最高有效数字更接近9而不是1。

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

    在选择范围[233…234)或[8.589934592e9…17.179869184e9)中,有223不同的浮点数线性间隔1024。

    9.999872000e9和
    9.999744000e9可以完全编码为flot和7位十进制文本。区别是
    0.000128000e9或128,000。

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

    示例:“9.999851e9”和“9.999850e9”都转换为flot9.999850496000e 09

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

    浮点-文本-浮点

    考虑一个“最坏情况”条件,其中文本包含的信息很少——它的最高有效数字接近1而不是9。

    在[8.0…16.0)范围内,有2个23或8,388,608个不同的浮点数线性间隔。

    在[10.0…11.0)范围内,有1/8×223或1,048,576个不同的浮点值值。

    在[10,000000…11,000000)范围内,使用8位有效十进制数字,有1,000,000个不同的文本值。

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

    需要9位小数。