Подтвердить что ты не робот

Печать с плавающей точкой, сохранение точности

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

Сколько цифр нужно печатать, чтобы сохранить точность исходного поплавка?

Так как float имеет 24 * (log(2) / log(10)) = 7.2247199 десятичные цифры точности, моя первоначальная мысль заключалась в том, что печати 8 цифр должно быть достаточно. Но если мне не повезло, те 0.2247199 распределяются слева и справа от 7 значащих цифр, поэтому я должен, вероятно, напечатать 9 десятичных цифр.

Правильно ли мой анализ? Является ли 9 десятичных цифр достаточным для всех случаев? Как printf("%.9g", x);?

Существует ли стандартная функция, которая преобразует float в строку с минимальным количеством десятичных цифр, необходимых для этого значения, в случаях, когда 7 или 8 достаточно, поэтому я не печатаю ненужные цифры?

Примечание. Я не могу использовать шестнадцатеричные литералы с плавающей запятой, потому что стандартный С++ не поддерживает их.

4b9b3361

Ответ 1

Чтобы гарантировать, что двоичный двоичный двоичный код > десятичной- > двоичной последовательности восстанавливает исходное двоичное значение, IEEE 754 требует


The original binary value will be preserved by converting to decimal and back again using:[10]

    5 decimal digits for binary16
    9 decimal digits for binary32
    17 decimal digits for binary64
    36 decimal digits for binary128

For other binary formats the required number of decimal digits is

    1 + ceiling(p*log10(2)) 

where p is the number of significant bits in the binary format, e.g. 24 bits for binary32.

В C функциями, которые вы можете использовать для этих преобразований, являются snprintf() и strtof/strtod/strtold().

Конечно, в некоторых случаях могут быть полезны даже больше цифр (нет, они не всегда "шумы", в зависимости от реализации десятичных программ преобразования, таких как snprintf()). Рассмотрим, например, печать диадических фракций.

Ответ 2

24 * (log (2)/log (10)) = 7,2247199

Это довольно репрезентативная проблема. Нет никакого смысла выражать количество значащих цифр с точностью до 0,0000001 цифры. Вы конвертируете числа в текст в интересах человека, а не машины. Человеку было все равно, и он предпочел бы, если бы вы написали

24 * (log (2)/log (10)) = 7

Попытка отобразить 8 значащих цифр просто генерирует цифры случайного шума. С ненулевыми коэффициентами 7 слишком много, потому что ошибка с плавающей запятой накапливается в вычислениях. Прежде всего, напечатайте цифры, используя разумную единицу измерения. Люди интересуются миллиметрами, граммами, фунтами, дюймами и т.д. Ни один архитектор не будет заботиться о размере окна, выраженного более точно, чем 1 мм. Ни один завод по производству окон не обещает окно с таким же точным размером.

И последнее, но не менее важное: вы не можете игнорировать точность чисел, которые вы подаете в свою программу. Измерение скорости пропитанной европейской ласточки до 7 цифр невозможно. Это примерно 11 метров в секунду, в лучшем случае 2 цифры. Поэтому выполнение вычислений на этой скорости и печать результата с более значительными цифрами приводит к бессмысленным результатам, которые обещают точность, которой нет.

Ответ 3

Если программа предназначена для чтения компьютером, я бы сделал простой прием использования char* aliasing.

  • псевдоним float* - char*
  • копировать в unsigned (или любой другой тип без знака достаточно велик) с помощью char* сглаживания
  • напечатать значение unsigned

Декодирование просто реверсирует процесс (и на большинстве платформ может использоваться прямой reinterpret_cast).

Ответ 4

Если у вас есть библиотека C, соответствующая C99 (и если ваши типы с плавающей точкой имеют базу, которая имеет мощность 2:), символ printf формата %a может печатать значения с плавающей запятой без отсутствия точности в шестнадцатеричную форму и утилит, так как scanf и strod смогут их прочитать.

Ответ 5

Преобразование с плавающей запятой в десятичное число, используемое в Java, гарантируется, что будет производиться наименьшее число десятичных цифр за десятичной точкой, необходимое для того, чтобы отличать число от своих соседей (более или менее).

Вы можете скопировать алгоритм отсюда: http://www.docjar.com/html/api/sun/misc/FloatingDecimal.java.html Обратите внимание на конструктор FloatingDecimal(float) и метод toJavaFormatString().

Ответ 6

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

Так как может быть несколько таких чисел, алгоритм также выберет ближайшую десятичную дробь в исходную двоичную дробь (I с именем float value).

Жаль, что в C нет такой стандартной библиотеки.

Ответ 7

Вы можете использовать sprintf. Я не уверен, что это точно ответит на ваш вопрос, но, в любом случае, вот пример кода

#include <stdio.h>
int main( void )
{
float d_n = 123.45;
char s_cp[13] = { '\0' };
char s_cnp[4] = { '\0' };
/*
* with sprintf you need to make sure there enough space
* declared in the array
*/
sprintf( s_cp, "%.2f", d_n );
printf( "%s\n", s_cp );
/*
* snprinft allows to control how much is read into array.
* it might have portable issues if you are not using C99
*/
snprintf( s_cnp, sizeof s_cnp - 1 , "%f", d_n );
printf( "%s\n", s_cnp );
getchar();
return 0;
}
/* output :
* 123.45
* 123
*/

Ответ 8

С чем-то вроде

def f(a):
    b=0
    while a != int(a): a*=2; b+=1
    return a, b

(это Python), вы сможете получить мантиссы и экспоненты без потерь.

В C это, вероятно, будет

struct float_decomp {
    float mantissa;
    int exponent;
}

struct float_decomp decomp(float x)
{
    struct float_decomp ret = { .mantissa = x, .exponent = 0};
    while x != floor(x) {
        ret.mantissa *= 2;
        ret.exponent += 1;
    }
    return ret;
}

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