15
15
MPZ_TWO , MPZ_ZERO , STRICT , gmpy , sage , sage_utils )
16
16
from .libintmath import (bctable , bin_to_radix , giant_steps , isqrt , isqrt_fast ,
17
17
lshift , numeral , rshift , sqrt_fixed , sqrtrem ,
18
- trailing , trailtable )
18
+ stddigits , trailing , trailtable )
19
19
20
20
21
21
class ComplexResult (ValueError ):
@@ -1028,13 +1028,13 @@ def mpf_perturb(x, eps_sign, prec, rnd):
1028
1028
# Radix conversion #
1029
1029
#----------------------------------------------------------------------------#
1030
1030
1031
- def to_digits_exp (s , dps ):
1031
+ def to_digits_exp (s , dps , base = 10 ):
1032
1032
"""Helper function for representing the floating-point number s as
1033
- a decimal with dps digits. Returns (sign, string, exponent) where
1034
- sign is '' or '-', string is the digit string, and exponent is
1035
- the decimal exponent as an int.
1033
+ a string with dps digits. Returns (sign, string, exponent) where
1034
+ sign is '' or '-', string is the digit string in the given base,
1035
+ and exponent is the exponent as an int.
1036
1036
1037
- If inexact, the decimal representation is rounded toward zero."""
1037
+ If inexact, the string representation is rounded toward zero."""
1038
1038
1039
1039
# Extract sign first so it doesn't mess up the string digit count
1040
1040
if s [0 ]:
@@ -1047,12 +1047,19 @@ def to_digits_exp(s, dps):
1047
1047
if not man :
1048
1048
return '' , '0' , 0
1049
1049
1050
- bitprec = int (dps * math .log (10 ,2 )) + 10
1050
+ if base == 10 :
1051
+ blog2 = 3.3219280948873626
1052
+ elif pow (2 , blog2 := int (math .log2 (base ))) == base :
1053
+ pass
1054
+ else :
1055
+ raise NotImplementedError
1056
+
1057
+ bitprec = int (dps * blog2 ) + 10
1051
1058
1052
1059
# Cut down to size
1053
1060
# TODO: account for precision when doing this
1054
1061
exp_from_1 = exp + bc
1055
- if abs (exp_from_1 ) > 3500 :
1062
+ if base == 10 and abs (exp_from_1 ) > 3500 :
1056
1063
from .libelefun import mpf_ln2 , mpf_ln10
1057
1064
1058
1065
# Set b = int(exp * log(2)/log(10))
@@ -1073,19 +1080,19 @@ def to_digits_exp(s, dps):
1073
1080
# fixed-point number and then converting that number to
1074
1081
# a decimal fixed-point number.
1075
1082
fixprec = max (bitprec - exp - bc , 0 )
1076
- fixdps = int (fixprec / math . log ( 10 , 2 ) + 0.5 )
1083
+ fixdps = int (fixprec / blog2 + 0.5 )
1077
1084
sf = to_fixed (s , fixprec )
1078
- sd = bin_to_radix (sf , fixprec , 10 , fixdps )
1079
- digits = numeral (sd , base = 10 , size = dps )
1085
+ sb = bin_to_radix (sf , fixprec , base , fixdps )
1086
+ digits = numeral (sb , base = base , size = dps )
1080
1087
1081
1088
exponent += len (digits ) - fixdps - 1
1082
1089
return sign , digits , exponent
1083
1090
1084
1091
def to_str (s , dps , strip_zeros = True , min_fixed = None , max_fixed = None ,
1085
- show_zero_exponent = False ):
1092
+ show_zero_exponent = False , base = 10 ):
1086
1093
"""
1087
- Convert a raw mpf to a decimal floating-point literal with at
1088
- most `dps` decimal digits in the mantissa (not counting extra zeros
1094
+ Convert a raw mpf to a floating-point literal in the given base
1095
+ with at most `dps` digits in the mantissa (not counting extra zeros
1089
1096
that may be inserted for visual purposes).
1090
1097
1091
1098
The number will be printed in fixed-point format if the position
@@ -1100,13 +1107,15 @@ def to_str(s, dps, strip_zeros=True, min_fixed=None, max_fixed=None,
1100
1107
by from_str, float() or Decimal().
1101
1108
"""
1102
1109
1110
+ sep = '@' if base > 10 else 'e'
1111
+
1103
1112
# Special numbers
1104
1113
if not s [1 ]:
1105
1114
if s == fzero :
1106
1115
if dps : t = '0.0'
1107
1116
else : t = '.0'
1108
1117
if show_zero_exponent :
1109
- t += 'e +0'
1118
+ t += sep + ' +0'
1110
1119
return t
1111
1120
if s == finf : return '+inf'
1112
1121
if s == fninf : return '-inf'
@@ -1118,23 +1127,27 @@ def to_str(s, dps, strip_zeros=True, min_fixed=None, max_fixed=None,
1118
1127
1119
1128
# to_digits_exp rounds to floor.
1120
1129
# This sometimes kills some instances of "...00001"
1121
- sign , digits , exponent = to_digits_exp (s , dps + 3 )
1130
+ sign , digits , exponent = to_digits_exp (s , dps + 3 , base )
1131
+
1132
+ rnd_digs = stddigits [(base // 2 + base % 2 ):base ]
1122
1133
1123
1134
# No digits: show only .0; round exponent to nearest
1124
1135
if not dps :
1125
- if digits [0 ] in '56789' :
1136
+ if digits [0 ] in rnd_digs :
1126
1137
exponent += 1
1127
1138
digits = ".0"
1128
1139
1129
1140
else :
1130
1141
# Rounding up kills some instances of "...99999"
1131
- if len (digits ) > dps and digits [dps ] in '56789' :
1142
+ if len (digits ) > dps and digits [dps ] in rnd_digs :
1132
1143
digits = digits [:dps ]
1133
1144
i = dps - 1
1134
- while i >= 0 and digits [i ] == '9' :
1145
+ dig = stddigits [base - 1 ]
1146
+ while i >= 0 and digits [i ] == dig :
1135
1147
i -= 1
1136
1148
if i >= 0 :
1137
- digits = digits [:i ] + str (int (digits [i ]) + 1 ) + '0' * (dps - i - 1 )
1149
+ digits = digits [:i ] + stddigits [int (digits [i ], base ) + 1 ] + \
1150
+ '0' * (dps - i - 1 )
1138
1151
else :
1139
1152
digits = '1' + '0' * (dps - 1 )
1140
1153
exponent += 1
@@ -1162,9 +1175,19 @@ def to_str(s, dps, strip_zeros=True, min_fixed=None, max_fixed=None,
1162
1175
if digits [- 1 ] == "." :
1163
1176
digits += "0"
1164
1177
1178
+ if base == 2 :
1179
+ prefix = "0b"
1180
+ elif base == 8 :
1181
+ prefix = "0o"
1182
+ elif base == 16 :
1183
+ prefix = "0x"
1184
+ else :
1185
+ prefix = ""
1186
+
1187
+ sign += prefix
1188
+
1165
1189
if exponent == 0 and dps and not show_zero_exponent : return sign + digits
1166
- if exponent >= 0 : return sign + digits + "e+" + str (exponent )
1167
- if exponent < 0 : return sign + digits + "e" + str (exponent )
1190
+ return sign + digits + sep + "{:+}" .format (exponent )
1168
1191
1169
1192
def str_to_man_exp (x , base = 10 ):
1170
1193
"""Helper function for from_str."""
0 commit comments