@@ -32,9 +32,17 @@ namespace openPMD
3232{
3333inline Datatype dtype_from_numpy (pybind11::dtype const dt)
3434{
35+ // Use explicit PEP 3118 / struct format character codes for portable
36+ // cross-platform behavior. This ensures consistency with
37+ // dtype_from_bufferformat() and avoids platform-dependent interpretation
38+ // of numpy type name strings.
39+ // ref: https://docs.python.org/3/library/struct.html#format-characters
40+ // ref: https://numpy.org/doc/stable/reference/arrays.interface.html
3541 // ref: https://docs.scipy.org/doc/numpy/user/basics.types.html
36- // ref: https://github.com/numpy/numpy/issues/10678#issuecomment-369363551
37- if (dt.char_ () == pybind11::dtype (" b" ).char_ ())
42+ char const c = dt.char_ ();
43+ switch (c)
44+ {
45+ case ' b' : // signed char
3846 if constexpr (std::is_signed_v<char >)
3947 {
4048 return Datatype::CHAR;
@@ -43,7 +51,7 @@ inline Datatype dtype_from_numpy(pybind11::dtype const dt)
4351 {
4452 return Datatype::SCHAR;
4553 }
46- else if (dt. char_ () == pybind11::dtype ( " B " ). char_ ())
54+ case ' B ' : // unsigned char
4755 if constexpr (std::is_unsigned_v<char >)
4856 {
4957 return Datatype::CHAR;
@@ -52,42 +60,41 @@ inline Datatype dtype_from_numpy(pybind11::dtype const dt)
5260 {
5361 return Datatype::UCHAR;
5462 }
55- else if (dt. char_ () == pybind11::dtype ( " short" ). char_ ())
63+ case ' h ' : // short
5664 return Datatype::SHORT;
57- else if (dt.char_ () == pybind11::dtype (" intc" ).char_ ())
58- return Datatype::INT;
59- else if (dt.char_ () == pybind11::dtype (" int_" ).char_ ())
60- return Datatype::LONG;
61- else if (dt.char_ () == pybind11::dtype (" longlong" ).char_ ())
62- return Datatype::LONGLONG;
63- else if (dt.char_ () == pybind11::dtype (" ushort" ).char_ ())
65+ case ' H' : // unsigned short
6466 return Datatype::USHORT;
65- else if (dt.char_ () == pybind11::dtype (" uintc" ).char_ ())
67+ case ' i' : // int
68+ return Datatype::INT;
69+ case ' I' : // unsigned int
6670 return Datatype::UINT;
67- else if (dt.char_ () == pybind11::dtype (" uint" ).char_ ())
71+ case ' l' : // long
72+ return Datatype::LONG;
73+ case ' L' : // unsigned long
6874 return Datatype::ULONG;
69- else if (dt.char_ () == pybind11::dtype (" ulonglong" ).char_ ())
75+ case ' q' : // long long
76+ return Datatype::LONGLONG;
77+ case ' Q' : // unsigned long long
7078 return Datatype::ULONGLONG;
71- else if (dt.char_ () == pybind11::dtype (" clongdouble" ).char_ ())
72- return Datatype::CLONG_DOUBLE;
73- else if (dt.char_ () == pybind11::dtype (" cdouble" ).char_ ())
74- return Datatype::CDOUBLE;
75- else if (dt.char_ () == pybind11::dtype (" csingle" ).char_ ())
76- return Datatype::CFLOAT;
77- else if (dt.char_ () == pybind11::dtype (" longdouble" ).char_ ())
78- return Datatype::LONG_DOUBLE;
79- else if (dt.char_ () == pybind11::dtype (" double" ).char_ ())
80- return Datatype::DOUBLE;
81- else if (dt.char_ () == pybind11::dtype (" single" ).char_ ())
79+ case ' f' : // float
8280 return Datatype::FLOAT;
83- else if (dt.char_ () == pybind11::dtype (" bool" ).char_ ())
81+ case ' d' : // double
82+ return Datatype::DOUBLE;
83+ case ' g' : // long double
84+ return Datatype::LONG_DOUBLE;
85+ case ' F' : // complex float
86+ return Datatype::CFLOAT;
87+ case ' D' : // complex double
88+ return Datatype::CDOUBLE;
89+ case ' G' : // complex long double
90+ return Datatype::CLONG_DOUBLE;
91+ case ' ?' : // bool
8492 return Datatype::BOOL;
85- else
86- {
93+ default :
8794 pybind11::print (dt);
8895 throw std::runtime_error (
89- std::string (" Datatype '" ) + dt. char_ () +
90- std::string (" ' not known in 'dtype_from_numpy'!" )); // _s.format(dt)
96+ std::string (" Datatype '" ) + c +
97+ std::string (" ' not known in 'dtype_from_numpy'!" ));
9198 }
9299}
93100
@@ -159,6 +166,8 @@ inline Datatype dtype_from_bufferformat(std::string const &fmt)
159166
160167inline pybind11::dtype dtype_to_numpy (Datatype const dt)
161168{
169+ // Use explicit PEP 3118 format character codes for portable behavior.
170+ // This ensures round-trip consistency with dtype_from_numpy().
162171 using DT = Datatype;
163172 switch (dt)
164173 {
@@ -177,80 +186,57 @@ inline pybind11::dtype dtype_to_numpy(Datatype const dt)
177186 case DT::SCHAR:
178187 case DT::VEC_SCHAR:
179188 return pybind11::dtype (" b" );
180- break ;
181189 case DT::UCHAR:
182190 case DT::VEC_UCHAR:
183191 return pybind11::dtype (" B" );
184- break ;
185- // case DT::SCHAR:
186- // case DT::VEC_SCHAR:
187- // pybind11::dtype("b");
188- // break;
189192 case DT::SHORT:
190193 case DT::VEC_SHORT:
191- return pybind11::dtype (" short" );
192- break ;
194+ return pybind11::dtype (" h" );
193195 case DT::INT:
194196 case DT::VEC_INT:
195- return pybind11::dtype (" intc" );
196- break ;
197+ return pybind11::dtype (" i" );
197198 case DT::LONG:
198199 case DT::VEC_LONG:
199- return pybind11::dtype (" int_" );
200- break ;
200+ return pybind11::dtype (" l" );
201201 case DT::LONGLONG:
202202 case DT::VEC_LONGLONG:
203- return pybind11::dtype (" longlong" );
204- break ;
203+ return pybind11::dtype (" q" );
205204 case DT::USHORT:
206205 case DT::VEC_USHORT:
207- return pybind11::dtype (" ushort" );
208- break ;
206+ return pybind11::dtype (" H" );
209207 case DT::UINT:
210208 case DT::VEC_UINT:
211- return pybind11::dtype (" uintc" );
212- break ;
209+ return pybind11::dtype (" I" );
213210 case DT::ULONG:
214211 case DT::VEC_ULONG:
215- return pybind11::dtype (" uint" );
216- break ;
212+ return pybind11::dtype (" L" );
217213 case DT::ULONGLONG:
218214 case DT::VEC_ULONGLONG:
219- return pybind11::dtype (" ulonglong" );
220- break ;
215+ return pybind11::dtype (" Q" );
221216 case DT::FLOAT:
222217 case DT::VEC_FLOAT:
223- return pybind11::dtype (" single" );
224- break ;
218+ return pybind11::dtype (" f" );
225219 case DT::DOUBLE:
226220 case DT::VEC_DOUBLE:
227221 case DT::ARR_DBL_7:
228- return pybind11::dtype (" double" );
229- break ;
222+ return pybind11::dtype (" d" );
230223 case DT::LONG_DOUBLE:
231224 case DT::VEC_LONG_DOUBLE:
232- return pybind11::dtype (" longdouble" );
233- break ;
225+ return pybind11::dtype (" g" );
234226 case DT::CFLOAT:
235227 case DT::VEC_CFLOAT:
236- return pybind11::dtype (" csingle" );
237- break ;
228+ return pybind11::dtype (" F" );
238229 case DT::CDOUBLE:
239230 case DT::VEC_CDOUBLE:
240- return pybind11::dtype (" cdouble" );
241- break ;
231+ return pybind11::dtype (" D" );
242232 case DT::CLONG_DOUBLE:
243233 case DT::VEC_CLONG_DOUBLE:
244- return pybind11::dtype (" clongdouble" );
245- break ;
234+ return pybind11::dtype (" G" );
246235 case DT::BOOL:
247- return pybind11::dtype (" bool" ); // also "?"
248- break ;
236+ return pybind11::dtype (" ?" );
249237 case DT::UNDEFINED:
250238 default :
251- throw std::runtime_error (
252- " dtype_to_numpy: Invalid Datatype '{...}'!" ); // _s.format(dt)
253- break ;
239+ throw std::runtime_error (" dtype_to_numpy: Invalid Datatype!" );
254240 }
255241}
256242} // namespace openPMD
0 commit comments