Какие числа с плавающей запятой одинарной точности требуют 9 значащих десятичных цифр для однозначного десятичного представления? - PullRequest
3 голосов
/ 21 марта 2020

Я нашел следующее утверждение в этой статье в Википедии о числах с плавающей запятой одинарной точности https://en.wikipedia.org/wiki/Single-precision_floating-point_format:

Если число IEEE 754 с одинарной точностью преобразуется в десятичная строка с как минимум 9 значащими цифрами, а затем преобразованная обратно в представление одинарной точности, конечный результат должен соответствовать исходному числу.

Я пытался найти примеры чисел с плавающей запятой одинарной точности которые на самом деле нуждаются в 9 значащих десятичных разрядах и уже не являются однозначными только с 8 значащими цифрами и не нашли ни одного, например, путем печати значений с плавающей запятой в отладчике gdb или путем попытки преобразования различных значений с одинарной точностью в октаве, но не нашли примеры, которым требуется более 8 десятичных цифр, чтобы иметь другое десятичное представление, нежели их значения с плавающей запятой прямого соседа.

Вопрос в том, существуют ли значения с плавающей запятой одинарной точности (32 бита), которые требуют 9 десятичных цифр, или это просто безопасная верхняя граница, которая никогда не нужна. Не могли бы вы привести пример значения с плавающей запятой одинарной точности, которое при преобразовании только в 8 значащих десятичных цифр и последующем преобразовании обратно в двоичное представление с плавающей запятой имеет значение, отличное от исходного значения с плавающей запятой.

Ответы [ 2 ]

5 голосов
/ 21 марта 2020

32-битные числа с плавающей запятой хранятся в 32 битах, что означает, что не может быть намного больше, чем приблизительно 4 миллиарда различных значений. Компьютеры достаточно быстры, чтобы выполнять итерацию по всем числам, поэтому поиск с помощью грубой силы 32-разрядных чисел с плавающей запятой может автоматизировать это за приемлемое время и проверить все возможные числа, если преобразование в строку содержит только 8 значащих десятичных цифр плюс обратное преобразование обратно из строки в представление с плавающей запятой одинарной точности изменяет значение.

Следующая короткая программа C ++ делает это для всех положительных значений с плавающей запятой:

#include <cstdio>
#include <cmath>
#include <limits>
#include <cinttypes>

int main(int argc, char**argv) {
  // Test if conversion with /precision/ significant decimal digit is enough
  int precision = 8;

  // Can override precision = 8 with a command line parameter
  if (argc > 1) {
    precision = strtol(argv[1], nullptr, 0);
    if (precision < 1 || precision > 50) {
      printf("Error: precision should be between 1 and 50, got %d.\n",
             precision);
      exit(1);
    }
  }

  // Buffer length of character buffers to store string representations of
  // floating point numbers with /precision/ significant digits.  /buflen/ is
  // larger than /precision/ because it also needs to store leading zeros,
  // decimal point, sign, scientific notation exponents, and terminating \0.
  const int buflen = precision + 10;

  // storage for current number converted to string with 8 decimal digits
  char str[buflen] = "";

  // shorthands for maxfloat and infinity
  const float maxfloat = std::numeric_limits<float>::max();
  const float inf = std::numeric_limits<float>::infinity();

  // Count the number of times where /precision/ was not sufficient
  uint64_t num_clashes_found = 0;

  // Count all tested floats
  uint64_t num_floats_tested = 0;

  // loop over all positive single precision floating point numbers
  for (float f = 0.0f;               // start with zero
       f <= maxfloat;                // iterate up to and including maxfloat
       ++num_floats_tested,          // count the number of all tested floats
       f = nextafterf(f, inf))       // increment f to next larger float value 
  {
    // convert number to string with /precision/ significant decimal digits
    int numprintedchars = snprintf(str, buflen, "%.*g", precision, f);

    // If string buffer is not long enough to store number as string with
    // /precision/ significant digits, then print warning and terminate program
    if (numprintedchars >= buflen) {
      printf("Buffer length %d is not enough to store \"%.*g\", should"
             " be at least %d\n", buflen, precision, f, numprintedchars+1);
      exit(1);
    }

    // convert the string back to float
    float float_from_string = strtof(str,nullptr);

    // Compare the value
    if (f != float_from_string) {
      printf("%.*g converts to \"%s\" which reads back as %.*g.\n",
             precision+1, f, str, precision+1, float_from_string);
      ++num_clashes_found;
    }
  }
  printf("Found %" PRIu64" clashes when using %d significant decimal digits.\n",
         num_clashes_found, precision);
  printf("Total number of tested floats is %" PRIu64", i.e. with %d significant"
         " decimal digits, we get clashes in %g%% of all numbers.\n",
         num_floats_tested, precision,
         100.0 / num_floats_tested * num_clashes_found);
  return 0;
}

Этой программе требуется около 20 минут для перебора всех положительных чисел с плавающей запятой одинарной точности.

Один найденный пример - 0.111294314f. При преобразовании в десятичную строку с 8 значащими цифрами результат будет «0.11129431». Следующее меньшее число с плавающей запятой одинарной точности - 0.111294307f, которое имеет то же десятичное представление при преобразовании в строку только с 8 значащими цифрами.

В целом программа считает, что существует около 2,14 миллиарда положительных чисел с плавающей запятой , но только около 32 миллионов из них нуждаются в 9 значащих десятичных разрядах для однозначного представления. Это соответствует примерно 1,5% всех чисел, для которых нужны 9 цифр, что объясняет, почему при ручном тестировании их вряд ли можно найти:

Ясно, что можно вручную проверять значения с плавающей запятой, десятичные представления которых начинаются с ди git 1, потому что для них вам понадобится еще один значащий десятичный знак di git для ведущего 1 по сравнению с предыдущими значениями очень похожего значения, которые начинаются с di git 9. Однако существуют также степени 10, для которых не существует значения с плавающей запятой, которое преобразуется в десятичное значение 1.xxx * 10 ^ yy, для которого фактически требуется 9 значащих ди git. Эти степени 10, где всегда достаточно 8 значащих цифр (приведены показатели 10, названы yy выше): -34, -31, -21, -18, -15, -12, -09, -06, -05 , -03, +00, +07, +08, +10, +13, +16, +19, +22, +25, +28. Если случится вручную проверить значения вблизи любой из этих степеней 10, никаких положительных результатов найти не удастся. Это включает 10 ^ 0, то есть значения около 1,0, что, вероятно, является наиболее вероятным местом для людей, чтобы начать ручной поиск.

1 голос
/ 23 марта 2020

существуют ли на самом деле значения с плавающей запятой одинарной точности (32 бита), для которых требуется 9 десятичных цифр (?): OP

Я пытался найти примеры чисел с плавающей запятой одинарной точности которые на самом деле нуждаются в 9 значащих десятичных разрядах и уже не являются однозначными только с 8 значащими цифрами и не нашли ни одной: OP

Принцип голубиных отверстий

Пример: между 8 и 16 имеются 2 23 различные float, линейно распределенные благодаря двоичному кодированию общего float. 1/8 из них находятся между [10 и 11): 2 20 или 1 048 576 различных значений. Первые 2 десятичных знака 10. Использование только 6 десятичных цифр больше, как в 10.xxx xxx только для 1 000 000 различных комбинаций, но нам нужно 1 048 576. 48 576 из float значений вступают в противоречие с 48 576 другими. Требуется другое десятичное число git. Подробнее


C spe c использует нижеследующее для базовых 2 чисел FP, чтобы найти XXX_DECIMAL_DIG, который является поиском 9 OP.

количество десятичных цифр, n, так что любое число с плавающей запятой с p radix b может быть округлено до числа с плавающей запятой с n десятичных цифр и обратно без изменений к значению C17dr § 5.2.4.2.2 11

Для float используйте p == 24 для 24 значащих цифр, которые он кодирует. (Явно 23).

FLT_DECIMAL_DIG =

потолок (1 + p макс * журнал 10 2)

потолок (1 + 24 * 0,3010 ...)

потолок (8,224 ...)

9

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...