Правильно ли утверждать, что первое число, которое сталкивается с одинарной точностью, - это 131072.02? (положительный, считая 2 цифры мантиссой)

Я пытался выяснить для своего аудиоприложения, можно ли использовать float для правильного представления диапазона параметров, которые я буду использовать.

"Самая большая" маска, которая ему нужна, предназначена для параметров частоты, которая является положительной и допускает максимум две цифры в качестве мантиссы (то есть от 20.00 hz до 22000.00 hz). По идее, следующие цифры будут округлены, поэтому мне они не нужны.

Итак, я сделал этот сценарий, чтобы проверить первое совпадающее число с одинарной точностью:

float temp = 0.0;
double valueDouble = 0.0;
double increment = 1e-2;

bool found = false;
while(!found) {
    double oldValue = valueDouble;
    valueDouble += increment;
    float value = valueDouble;

    // found
    if(temp == value) {
        std::cout << "collision found: " << valueDouble << std::endl;
        std::cout << "   collide with: " << oldValue << std::endl;
        std::cout << "float stored as: " << value << std::endl;
        found = true;
    }

    temp = value;        
}

и, кажется, его 131072.02131072.01, сохраненным как то же значение 131072.015625), что намного дальше, чем 22000.00. И, похоже, я бы нормально использовал float.

Но хотелось бы понять, верны ли эти рассуждения. Это?

Вся проблема была бы в том, если бы я установил параметр XXXXX.YY (7 digits), и он столкнется с некоторыми другими числами, имеющими меньшее количество цифр (потому что single precision гарантирует только 6 digits)

Примечание: конечно числа, такие как 1024.0002998145910169114358723163604736328125 или 1024.000199814591042013489641249179840087890625, конфликтуют, и они находятся в пределах интервала, но они делают это с более длинными значащими цифрами, чем моя требуемая мантисса, поэтому мне все равно.

@JHBonarius, где ты видишь, как он жалуется?

Max Langhof 11.04.2018 12:04

Для справки прочтите это: randomascii.wordpress.com/2012/03/21/…, а также эти: randomascii.wordpress.com/category/floating-point (не мой блог)

Richard Critten 11.04.2018 12:14

"т.е. от 20.00 Гц до 22000.00 Гц" - должен используется как целое число в сантигерцах. Это целое число будет иметь диапазон [2000, 2200000], и никаких проблем с округлением не будет. Почему? Потому что float не может даже правильно представлять 0.2!

cmaster - reinstate monica 11.04.2018 12:23

"максимум две цифры в виде мантиссы (т.е. от 20.00 Гц до 22000.00 Гц)". Эх, нет, это не двухзначная мантисса. 22000.00 Hz будет 2.200000E5, поэтому мантисса состоит из 7 цифр, а показатель степени - из одной цифры. За исключением маленького бита, когда компьютеры используют основание 2 и биты, а не основание 10 и цифры.

MSalters 11.04.2018 15:37
4
4
156
3

Ответы 3

IEEE 754Одинарная точность определяется как

  • 1 знаковый бит
  • 8 разрядов экспоненты: диапазон от 2 ^ -126 до 2 ^ 127 ~ 10 ^ -38 до 10 ^ 38)
  • 23 дробных (мантисса) бита: десятичная точность в зависимости от степени)

При 22k экспонента будет представлять смещение 16384 = 2 ^ 14, поэтому 23-битная мантисса даст вам точность 2 ^ 14/2 ^ 23 = 1/2 ^ 9 = 0,001953125 ... что достаточно для вашего дело.

Для 131072.01 показатель степени будет представлять смещение 131072 = 2 ^ 17, поэтому мантисса даст точность 2 ^ 17/2 ^ 23 = 1/2 ^ 6 = 0,015625, что больше, чем ваша целевая точность 0,01.

Объяснение того, почему тест OP не согласен (накопленная ошибка), улучшило бы этот ответ.

Yakk - Adam Nevraumont 11.04.2018 12:52

@Yakk Ты это имеешь в виду?

JHBonarius 11.04.2018 14:22

Поскольку мы имеем дело с относительной точностью, нет смысла иметь дело с неточными дробями, обнаруженными в вопросе. IOW, с 24 битами, все целые числа до ~ 16 миллионов могут быть представлены точно в виде числа с плавающей запятой одинарной точности. Поскольку для OP требуется всего 2 200 000 сантигерц, этого более чем достаточно.

aka.nice 11.04.2018 23:40

Ваша программа не проверяет, что именно вы хотите, но ваша основная аргументация должна быть в порядке.

Проблема с программой заключается в том, что valueDouble будет накапливать небольшие ошибки (поскольку 0,01 отображается неточно) - и преобразование строки «20.01» в число с плавающей запятой приведет к небольшим ошибкам округления.

Но эти ошибки должны быть порядка DBL_EPSILON и быть намного меньше, чем ошибка, которую вы видите.

Если вы действительно хотите его протестировать, вам нужно будет написать от «20.00» до «22000.00» и отсканировать их все, используя вариант scanf, который вы планируете использовать, и убедиться, что они различаются.

«Накопление ошибок» не имеет значения, поскольку результат (как напечатанный) учитывает эту ошибку. Накопление ошибок означает только то, что 131072 не будет достигнут точно за 13107200 шаги.

MSalters 11.04.2018 15:40

@MSalters: накопление ошибок имеет значение (в общем, в данном случае не обязательно с конкретными числами). Это означает, что значения в valueDouble не будут числами, которые OP должен тестировать. Теоретически может быть какая-то точка, где проверяемыми числами должны быть a и b, но ошибки приводят к проверке a + e0 и b + e1, и a и b сталкиваются, а a + e0 и b + e1 - нет, или наоборот. -versa, значит результат неверный. Тест на коллизию выполняется перед печатью чего-либо, поэтому преобразование для вывода не влияет на тесты.

Eric Postpischil 11.04.2018 16:54

@EricPostpischil: Это не имеет смысла. x и x+0.01 конфликтуют, поскольку x+0.01 отличается только 25-м битом. Вспомните, как работает сложение: у наименьшего числа показатель степени корректируется до наибольшего числа, мантисса сдвигается вправо. Как только этот сдвиг составляет 25 разрядов, вы добавляете 0, и возникает коллизия. Все числа x с одинаковой величиной вызовут одно и то же «сдвинуть на 25 позиций, добавить 0».

MSalters 11.04.2018 17:11

@MSalters: Во-первых, если у вас есть x и x+.01, они не отличаются на 0,01, поскольку x+.01 обязательно имеет ошибку округления в двоичной системе с плавающей запятой. Таким образом, x и x+0.01 могут отличаться 25-м битом, а x и x + .01 не отличаются 25-м битом. Во-вторых, даже если и пара x и x+.01, и пара x и x + .01 отличаются 25-м битом, x имеет некоторую накопленную ошибку, и она стоит вместо идеального у, который мы имели бы на этом этапе программы, и у и у + .01 могут не отличаться в 25-м бите, даже если x и x + .01 отличаются, или наоборот.

Eric Postpischil 11.04.2018 17:37

Is it correct to state that the first number that collide in single precision is 131072.02? (positive, considering 2 digits as mantissa after the decimal point)

Да.


I'd like to understand if that reasoning is correct. It is?

Для значений чуть меньше 131072.0f каждое последующее представимое значение float отстоит на 1/128.

Для значений в диапазоне [131072.0f ... 2 * 131072.0f) каждое последующее представимое значение float отстоит на 1/64.

Со значениями десятичной текстовой формы «131072.xx» существует 100 комбинаций, но только 64 отличаются от float. Неудивительно, что встречается 100-64 или 36 столкновения - см. Ниже. Для чисел этой формы это первое место, где плотность float слишком мала: младший бит в float> 0,01 в этом диапазоне.


int main(void) {
  volatile float previous = 0.0;
  for (long i = 1; i <= 99999999; i++) {
    volatile float f1 = i / 100.0;
    if (previous == f1) {
      volatile float f0 = nextafterf(f1, 0);
      volatile float f2 = nextafterf(f1, f1 * 2);
      printf("%f %f %f    delta fraction:%f\n", f0, f1, f2, 1.0 / (f1 - f0));
      static int count = 100 - 64;
      if (--count == 0) return 0;
    }
    previous = f1;
  }
  printf("Done\n");
}

Выход

131072.000000 131072.015625 131072.031250    delta fraction:64.000000
131072.031250 131072.046875 131072.062500    delta fraction:64.000000
131072.046875 131072.062500 131072.078125    delta fraction:64.000000
...
131072.921875 131072.937500 131072.953125    delta fraction:64.000000
131072.937500 131072.953125 131072.968750    delta fraction:64.000000
131072.968750 131072.984375 131073.000000    delta fraction:64.000000

Почему значащие числа числа с плавающей запятой 7 или 6 также может помочь.

Другие вопросы по теме