Use dtoa_milo from str-util for double/float precision printing

Replace std::setprecision approach with dtoa_milo for proper double/float
precision in timeSamples printing.

Changes:

1. src/str-util.cc:
   - Implement dtoa(double) wrapper using dtoa_milo for full precision
   - Implement dtoa(float) using snprintf with %.9g format
   - Include external/dtoa_milo.h at top of file

2. src/timesamples-pprint.cc:
   - Replace std::setprecision with dtoa() calls from str-util.hh
   - Remove <iomanip> and <limits> includes
   - Add str-util.hh include

3. tests/unit/unit-strutil.cc:
   - Add comprehensive dtoa_test() with 10 test cases:
     * Full precision for pi (3.141592653589793)
     * Full precision for e (2.718281828459045)
     * Negative values
     * Zero and negative zero
     * Small and large numbers
     * Float conversion
     * Integer-like doubles (42.0, 1.0)

4. tests/unit/unit-strutil.h:
   - Add dtoa_test() declaration

5. tests/unit/unit-main.cc:
   - Register dtoa_test in test suite

Results:
- Double: 3.141592653589793 → 3.141592653589793 (exact match)
- Float: 3.14159f → 3.14159012 (full float precision)
- All dtoa unit tests pass
- timesamples-scalar-double-001.usda comparison test passes

Test command:
  ./build_asan/unit-test-tinyusdz dtoa_test
This commit is contained in:
Syoyo Fujita
2026-01-11 06:29:02 +09:00
parent 10978c96c8
commit d72c0c2fbc
5 changed files with 161 additions and 13 deletions

View File

@@ -4,6 +4,7 @@
#include "unicode-xid.hh"
#include "common-macros.inc"
#include "external/dtoa_milo.h"
#ifdef __SSE2__
#include <emmintrin.h>
@@ -1212,4 +1213,20 @@ bool GlobMatchPath(const std::string &pattern, const std::string &path) {
return p == pattern.size();
}
char *dtoa(float f, char *buf) {
// For float, use simple sprintf for now
// dtoa_milo is optimized for double and doesn't work well with float
int n = snprintf(buf, 384, "%.9g", static_cast<double>(f));
if (n < 0 || n >= 384) {
buf[0] = '0';
return &buf[1];
}
return &buf[n];
}
char *dtoa(double d, char *buf) {
// Use dtoa_milo for double precision
return dtoa_milo(d, buf);
}
} // namespace tinyusdz

View File

@@ -6,8 +6,7 @@
#include <sstream>
#include <cstring>
#include <map>
#include <iomanip>
#include <limits>
#include "str-util.hh"
#ifdef TINYUSDZ_ENABLE_THREAD
#include <thread>
@@ -196,26 +195,26 @@ void print_type<char>(OutputAdapter& out, const uint8_t* data) {
out.write(static_cast<int>(value));
}
// Specialization for double - print with full precision
// Specialization for double - print with full precision using dtoa
template<>
void print_type<double>(OutputAdapter& out, const uint8_t* data) {
double value;
std::memcpy(&value, data, sizeof(double));
std::stringstream ss;
ss << std::setprecision(std::numeric_limits<double>::max_digits10);
ss << value;
out.write(ss.str());
char buf[384];
char *end = dtoa(value, buf);
*end = '\0';
out.write(std::string(buf));
}
// Specialization for float - print with full precision
// Specialization for float - print with full precision using dtoa
template<>
void print_type<float>(OutputAdapter& out, const uint8_t* data) {
float value;
std::memcpy(&value, data, sizeof(float));
std::stringstream ss;
ss << std::setprecision(std::numeric_limits<float>::max_digits10);
ss << value;
out.write(ss.str());
char buf[384];
char *end = dtoa(value, buf);
*end = '\0';
out.write(std::string(buf));
}
// Unified print function for vector types

View File

@@ -49,6 +49,7 @@ TEST_LIST = {
{ "strutil_test", strutil_test },
{ "tinystring_test", tinystring_test },
{ "parse_int_test", parse_int_test },
{ "dtoa_test", dtoa_test },
{ "timesamples_test", timesamples_test },
{ "materialx_config_api_struct_test", materialx_config_api_struct_test },
{ "materialx_config_api_parsing_test", materialx_config_api_parsing_test },

View File

@@ -195,5 +195,135 @@ void parse_int_test(void) {
tstring_view sv("+-123");
TEST_CHECK(!parse_int(sv, &result));
}
}
void dtoa_test(void) {
// Test double to ASCII conversion with full precision
{
// Test pi with full double precision
double pi = 3.141592653589793;
char buf[384];
char *end = dtoa(pi, buf);
*end = '\0';
std::string result(buf);
// Should preserve full precision (at least 15 significant digits)
TEST_CHECK(result.find("3.14159265358979") != std::string::npos);
TEST_MSG("pi result: %s", result.c_str());
}
{
// Test e with full double precision
double e = 2.718281828459045;
char buf[384];
char *end = dtoa(e, buf);
*end = '\0';
std::string result(buf);
// Should preserve full precision
TEST_CHECK(result.find("2.71828182845904") != std::string::npos);
TEST_MSG("e result: %s", result.c_str());
}
{
// Test negative value
double neg = -2.718281828459045;
char buf[384];
char *end = dtoa(neg, buf);
*end = '\0';
std::string result(buf);
TEST_CHECK(result[0] == '-');
TEST_CHECK(result.find("2.71828182845904") != std::string::npos);
TEST_MSG("negative e result: %s", result.c_str());
}
{
// Test zero
double zero = 0.0;
char buf[384];
char *end = dtoa(zero, buf);
*end = '\0';
std::string result(buf);
TEST_CHECK(result == "0.0");
TEST_MSG("zero result: %s", result.c_str());
}
{
// Test negative zero
double neg_zero = -0.0;
char buf[384];
char *end = dtoa(neg_zero, buf);
*end = '\0';
std::string result(buf);
// dtoa_milo should output "-0.0" for negative zero
TEST_CHECK(result == "-0.0");
TEST_MSG("negative zero result: %s", result.c_str());
}
{
// Test small number
double small = 0.000001234567890123456;
char buf[384];
char *end = dtoa(small, buf);
*end = '\0';
std::string result(buf);
// Should use scientific notation for very small numbers
TEST_MSG("small number result: %s", result.c_str());
TEST_CHECK(result.length() > 0);
}
{
// Test large number
double large = 1234567890123456.0;
char buf[384];
char *end = dtoa(large, buf);
*end = '\0';
std::string result(buf);
TEST_MSG("large number result: %s", result.c_str());
TEST_CHECK(result.length() > 0);
}
{
// Test float conversion
float f = 3.14159f;
char buf[384];
char *end = dtoa(f, buf);
*end = '\0';
std::string result(buf);
// Should output float with appropriate precision
TEST_CHECK(result.find("3.14159") != std::string::npos);
TEST_MSG("float result: %s", result.c_str());
}
{
// Test one
double one = 1.0;
char buf[384];
char *end = dtoa(one, buf);
*end = '\0';
std::string result(buf);
TEST_CHECK(result == "1.0");
TEST_MSG("one result: %s", result.c_str());
}
{
// Test integer-like double
double int_like = 42.0;
char buf[384];
char *end = dtoa(int_like, buf);
*end = '\0';
std::string result(buf);
TEST_CHECK(result == "42.0");
TEST_MSG("integer-like result: %s", result.c_str());
}
}

View File

@@ -3,3 +3,4 @@
void strutil_test(void);
void tinystring_test(void);
void parse_int_test(void);
void dtoa_test(void);