mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
IfcParse: serialize REALs with shortest round-trip form #7696
format_double formatted doubles with setprecision(max_digits10) (17 digits), which padded clean values with noise: 0.0174532925199433 was rewritten as 0.017453292519943299 and 1.E-05 as 1.0000000000000001E-05. Every REAL in a file changed on save, producing enormous diffs for anyone version-controlling IFC. Use std::to_chars, which emits the shortest string that round-trips exactly (like Python's repr), then keep the existing mantissa/exponent formatting. Verified in a standalone compile of the exact function logic: the reporter's values become 0.0174532925199433 and 1.E-05, 0.1 stays 0.1, and every tested value (including a denormal) round-trips back to the identical double. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
Thomas Krijnen
parent
b1be7d92e6
commit
ee2b357d74
@@ -39,6 +39,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <charconv>
|
||||||
|
|
||||||
#ifdef USE_MMAP
|
#ifdef USE_MMAP
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
@@ -753,11 +754,16 @@ namespace {
|
|||||||
// the output of the C++ ostream formatting operation.
|
// the output of the C++ ostream formatting operation.
|
||||||
// REAL = [ SIGN ] DIGIT { DIGIT } "." { DIGIT } [ "E" [ SIGN ] DIGIT { DIGIT } ] .
|
// REAL = [ SIGN ] DIGIT { DIGIT } "." { DIGIT } [ "E" [ SIGN ] DIGIT { DIGIT } ] .
|
||||||
static std::string format_double(const double& d) {
|
static std::string format_double(const double& d) {
|
||||||
|
// Use the shortest representation that round-trips exactly (like
|
||||||
|
// Python's repr) instead of max_digits10. max_digits10 padded clean
|
||||||
|
// values with noise digits (0.0174532925199433 -> 0.017453292519943299),
|
||||||
|
// which rewrote every REAL and produced huge diffs when a file was
|
||||||
|
// re-saved. See #7696.
|
||||||
|
char buf[64];
|
||||||
|
const auto res = std::to_chars(buf, buf + sizeof(buf), d);
|
||||||
|
const std::string str(buf, res.ptr);
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss.imbue(std::locale::classic());
|
oss.imbue(std::locale::classic());
|
||||||
oss << std::setprecision(std::numeric_limits<double>::max_digits10) << d;
|
|
||||||
const std::string str = oss.str();
|
|
||||||
oss.str("");
|
|
||||||
std::string::size_type e = str.find('e');
|
std::string::size_type e = str.find('e');
|
||||||
if (e == std::string::npos) {
|
if (e == std::string::npos) {
|
||||||
e = str.find('E');
|
e = str.find('E');
|
||||||
|
|||||||
Reference in New Issue
Block a user