@@ -1778,20 +1778,132 @@ double precise_xstrtod(const char *str, char **endptr, char decimal,
1778
1778
return number ;
1779
1779
}
1780
1780
1781
+ /* copy a decimal number string in form `decimal` and `tsep` and `sci` as
1782
+ decimal point, thousands separator and sci exponent character to a an
1783
+ equivalent c-locale decimal string (striping tsep, replacing `decimal`
1784
+ with '.' and sci with 'e'.
1785
+ */
1786
+
1787
+ char * str_copy_decimal_str_c (const char * s , char * * endpos , char decimal , char tsep , char sci ) {
1788
+ #define IS_TSEP (c ) (tsep != '\0' && c == tsep)
1789
+ ssize_t size = 0 ;
1790
+ const char * p = s ;
1791
+ // First count how many characters we can consume.
1792
+ // Leading sign
1793
+ if (* p == '+' || * p == '-' ) p ++ ;
1794
+ // Integer part
1795
+ while (isdigit_ascii (* p )) {
1796
+ p ++ ;
1797
+ p += IS_TSEP (* p );
1798
+ }
1799
+ // Fractional part
1800
+ if (* p == decimal ) {
1801
+ p ++ ;
1802
+ while (isdigit_ascii (* p )) {
1803
+ p ++ ;
1804
+ p += IS_TSEP (* p );
1805
+ }
1806
+ }
1807
+ // Exponent part
1808
+ if (toupper_ascii (* p ) == toupper_ascii (sci )) {
1809
+ p ++ ;
1810
+ // Exponent sign
1811
+ if (* p == '+' || * p == '-' ) p ++ ;
1812
+ // Exponent
1813
+ while (isdigit_ascii (* p )) {
1814
+ p ++ ;
1815
+ p += IS_TSEP (* p );
1816
+ }
1817
+ }
1818
+
1819
+ size = p - s ;
1820
+ char * pc = malloc (size + 1 );
1821
+ memcpy (pc , p , size );
1822
+ pc [size ] = '\0' ;
1823
+ char * dst = pc ;
1824
+ p = s ;
1825
+
1826
+ // Copy leading sign
1827
+ if (* p == '+' || * p == '-' ) {
1828
+ * dst ++ = * p ++ ;
1829
+ }
1830
+ // Copy integer part
1831
+ while (isdigit_ascii (* p )) {
1832
+ * dst ++ = * p ++ ;
1833
+ p += IS_TSEP (* p );
1834
+ }
1835
+ // Copy factional part, replacing `decimal` with '.'
1836
+ if (* p == decimal ) {
1837
+ * dst ++ = '.' ;
1838
+ p ++ ;
1839
+ while (isdigit_ascii (* p )) {
1840
+ * dst ++ = * p ++ ;
1841
+ p += IS_TSEP (* p );
1842
+ }
1843
+ }
1844
+ // Copy exponent replacing `sci` with 'e'
1845
+ if (toupper_ascii (* p ) == toupper_ascii (sci )) {
1846
+ * dst ++ = 'e' ;
1847
+ p ++ ;
1848
+ // Copy leading exponent sign
1849
+ if (* p == '+' || * p == '-' ) {
1850
+ * dst ++ = * p ++ ;
1851
+ }
1852
+ // Exponent
1853
+ while (isdigit_ascii (* p )) {
1854
+ * dst ++ = * p ++ ;
1855
+ p += IS_TSEP (* p );
1856
+ }
1857
+ }
1858
+ * dst = '\0' ;
1859
+ if (endpos != NULL ) {
1860
+ * endpos = (char * )p ;
1861
+ }
1862
+ return pc ;
1863
+ #undef IS_TSEP
1864
+ }
1865
+
1781
1866
double round_trip (const char * p , char * * q , char decimal , char sci , char tsep ,
1782
1867
int skip_trailing , int * error , int * maybe_int ) {
1868
+ char * pc = NULL ;
1869
+ // 'normalize' representation to C-locale; replace decimal with '.' and
1870
+ // remove t(housand)sep.
1871
+ char * endptr = NULL ;
1872
+ if (decimal != '.' || tsep != '\0' ) {
1873
+ pc = str_copy_decimal_str_c (p , & endptr , decimal , tsep , sci );
1874
+ }
1783
1875
// This is called from a nogil block in parsers.pyx
1784
1876
// so need to explicitly get GIL before Python calls
1785
1877
PyGILState_STATE gstate ;
1786
1878
gstate = PyGILState_Ensure ();
1787
-
1788
- double r = PyOS_string_to_double (p , q , 0 );
1879
+ double r ;
1880
+ if (pc != NULL ) {
1881
+ char * endpc = NULL ;
1882
+ r = PyOS_string_to_double (pc , & endpc , 0 );
1883
+ // PyOS_string_to_double needs to consume the whole string
1884
+ if (endpc == pc + strlen (pc )) {
1885
+ if (q != NULL ) {
1886
+ // report endptr from source string (p)
1887
+ * q = (char * ) endptr ;
1888
+ }
1889
+ } else {
1890
+ * error = -1 ;
1891
+ if (q != NULL ) {
1892
+ // p and pc are different len due to tsep removal. Can't report
1893
+ // how much it has consumed of p. Just rewind to beginning.
1894
+ * q = (char * )p ;
1895
+ }
1896
+ }
1897
+ } else {
1898
+ r = PyOS_string_to_double (p , q , 0 );
1899
+ }
1789
1900
if (maybe_int != NULL ) * maybe_int = 0 ;
1790
1901
if (PyErr_Occurred () != NULL ) * error = -1 ;
1791
1902
else if (r == Py_HUGE_VAL ) * error = (int )Py_HUGE_VAL ;
1792
1903
PyErr_Clear ();
1793
1904
1794
1905
PyGILState_Release (gstate );
1906
+ free (pc );
1795
1907
return r ;
1796
1908
}
1797
1909
0 commit comments