c++ - числа - число с плавающей точкой двойной точности




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

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

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

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

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

Мысли?

[Изменить] - как только я выясню, какое из предложенных решений лучше для меня, я отмечу одно как «решение».


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


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

#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 с возможностью точного восстановления на платформе, для которой компилируется код. Это дает намного более короткий вывод и сохраняет столько информации, сколько может однозначно представить double.

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

#include <stdlib.h>

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

Помимо представления хранилища, как насчет этого. Специальные значения, такие как -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] );
}

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


Чтобы напечатать длинные списки чисел в C ++ без потерь (писать и читать в одной и той же архитектуре), я использую это (для 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);






file-format