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

Лучший способ вывода полной точности в текстовый файл

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

Итак... у любого из вас есть хороший способ кодирования двойника как строки символов (помимо повышения точности). Моя первая мысль заключалась в том, чтобы сдать двойник на char [] и записать символы. Я не думаю, что это сработает, потому что некоторые из символов не видны, производят звуки и даже прерывают строки ('\ 0'... Я говорю с вами!)

Мысли?

[Edit] - как только я выясню, какое из предлагаемых решений лучше всего подходит для меня, я буду отмечать его как "решение".

4b9b3361

Ответ 1

Если вы хотите, чтобы формат был строго читаемым человеком, вы можете записать двойной:

#include <iomanip>
#include <sstream>

std::string doubleToText(const double & d)
{
    std::stringstream ss;
    //ss << std::setprecision( std::numeric_limits<double>::digits10+2);
    ss << std::setprecision( std::numeric_limits<int>::max() );
    ss << d;
    return ss.str();
}

std::numeric_limits<int>::max() будет выводиться с максимально возможной десятичной точностью. Это наиболее точно сохранит значение в разных реализациях с плавающей запятой. Перестановка этой строки для строки с комментариями с помощью std::numeric_limits<double>::digits10+2 даст достаточно точности, чтобы сделать двойную точную восстанавливаемую на платформе, с которой компилируется код. Это дает намного более короткий выход и сохраняет как можно больше информации, которую может однозначно представлять double.

Операторы потока С++ не сохраняют денормализованные числа или бесконечности и не-цифры при чтении строк. Однако функция POSIX strtod выполняется и определяется стандартом. Следовательно, наиболее точным способом чтения десятичного числа со стандартным вызовом библиотеки была бы эта функция:

#include <stdlib.h>

double textToDouble(const std::string & str)
{
    return strtod( str.c_str(), NULL );
}

Ответ 2

Предполагая, что IEEE 754 double, printf("%.17g\n", x) предоставит вам достаточно цифр для воссоздания исходного значения.

Ответ 3

Двухэтапный процесс: сначала используйте двоичную флотацию/двойную сериализацию, а затем примените base 64 кодирование. Результат не читается человеком, но не потеряет точность.

Изменить: (Благодаря fuzzyTew и dan04)

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

Ответ 4

Вы можете использовать базу 64. Это позволит вам хранить точные байтовые значения в текстовом файле.

Я не использовал его, но я нашел эту базу 64 кодирования/декодирования library для С++.

Ответ 5

Я был уверен, что существует специальный спецификатор формата для printf (возможно, %a?), который позволяет печатать двоичное представление float, но я не могу его найти..
Однако вы можете попробовать следующее:

int main(int argc, char* argv[]){
    union fi {
        unsigned int i;
        float        f;
    } num;
    num.f = 1.23f;
    printf("%X\n", num.i);
    return 0;
}

Ответ 6

Чтобы печатать длинные списки чисел на С++ без потерь (писать и читать в одной и той же архитектуре), я использую это (для double s):

#include<iostream>
#include<iomanip>
#include<limits>
#include<cmath>

#include<sstream>
int main(){
std::ostringstream oss;

int prec = std::numeric_limits<double>::digits10+2; // generally 17

int exponent_digits = std::log10(std::numeric_limits<double>::max_exponent10)+1; // generally 3
int exponent_sign   = 1; // 1.e-123
int exponent_symbol = 1; // 'e' 'E'
int digits_sign = 1;
int digits_dot = 1; // 1.2

int division_extra_space = 1;
int width = prec + exponent_digits + digits_sign + exponent_sign + digits_dot + exponent_symbol + division_extra_space;

double original = -0.000013213213e-100/33215.;
oss << std::setprecision(prec) << std::setw(width) << original << std::setw(width) << original << std::setw(width) << original << '\n';
oss << std::setprecision(prec) << std::setw(width) << 1. << std::setw(width) << 2. << std::setw(width) << -3. << '\n';
}

печатает

 -3.9780861056751466e-110 -3.9780861056751466e-110 -3.9780861056751466e-110
                        1                        2                       -3

В общем, в моем случае это похоже на настройку:

oss << std::precision(17) << std::setw(25) << original << ...;

В любом случае я могу проверить, работает ли это:

    std::istringstream iss(oss.str());
    double test; iss >> test;
    assert(test == original);

Ответ 7

Попробуйте следующее:

double d = 0.2512958125912;
std::ostringstream s;
s << d;

Затем напишите s в файл.

Ответ 8

Вы не говорите, почему двоичный файл отключен. Для вашего приложения можно было бы преобразовать двоичный код в шестнадцатеричную строку ASCII?

Ответ 9

Резервирование хранилища в стороне, как насчет этого. Специальные значения, такие как -0, бесконечности, NaN и т.д., Потребуют специальных однако. Также я "забыл" реализовать отрицательные показатели.

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

const int SCALE = 1<<(52/2);

void put( double a ) {
  FILE* f = fopen( "dump.txt", "wb" );
  int sign = (a<0); if( sign ) a=-a;
  int exp2 = 0; while( a>1 ) a/=2, exp2++;
  a*=SCALE;
  int m1 = floor(a);
  a = (a-m1)*SCALE;
  int m2 = floor(a);
  fprintf(f, "%i %i %i %i\n", sign, exp2, m1, m2 );
  fclose(f);
}

double get( void ) {
  FILE* f = fopen( "dump.txt", "rb" );
  double a;
  int sign, exp2, m1, m2;
  fscanf( f, "%i %i %i %i\n", &sign, &exp2, &m1, &m2 );
  fclose(f);
  printf( "%i %i %i %i\n", sign, exp2, m1, m2 );
  a = m2; a /= SCALE;
  a+= m1; a /= SCALE;
  while( exp2>0 ) a*=2, exp2--;
  if( a<0 ) a=-a;
  return a;
}

int main( void ) {
  union {
    double a;
    unsigned b[2];
  };
  a = 3.1415926;
  printf( "%.20lf %08X %08X\n", a, b[0], b[1] );
  put( a );
  a = get();
  printf( "%.20lf %08X %08X\n", a, b[0], b[1] );
}