mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
Improve float.fromhex module representation case
- Embrace hexadecimal without '0x', '.', 'p' - Embrace variety of 'nan', 'inf' case - Still overflow and near min, max, 0, 1 case isn't handled
This commit is contained in:
1 parent
24cc67980c
commit
bbd274a498
2 files changed
+233
-30
No files matched your search
+194
-17
@@ -2,6 +2,10 @@ import math
|
||||
|
||||
from testutils import assert_raises
|
||||
|
||||
NAN = float('nan')
|
||||
INF = float('inf')
|
||||
NINF = float('-inf')
|
||||
|
||||
1 + 1.1
|
||||
|
||||
a = 1.2
|
||||
@@ -240,21 +244,194 @@ a = 3.1__4
|
||||
with assert_raises(SyntaxError):
|
||||
exec(src)
|
||||
|
||||
assert float.fromhex('0.0') == 0.0
|
||||
assert float.fromhex('-0.0') == 0.0
|
||||
assert float.fromhex('0x0p0') == 0.0
|
||||
assert float.fromhex('0x010p0') == 16.0
|
||||
assert float.fromhex('0x0.10p3') == 0.5
|
||||
assert float.fromhex('-0x0p0') == 0.0
|
||||
assert float.fromhex('0x0.p0') == 0.0
|
||||
assert float.fromhex('-0x0.p0') == 0.0
|
||||
assert float.fromhex('0x0.0p+0') == 0.0
|
||||
assert float.fromhex('-0x0.0p+0') == -0.0
|
||||
assert float.fromhex('0x1.000000p+0') == 1.0
|
||||
assert float.fromhex('-0x1.800000p+0') == -1.5
|
||||
assert float.fromhex('inf') == float('inf')
|
||||
assert math.isnan(float.fromhex('nan'))
|
||||
assert_raises(ValueError, lambda: float.fromhex('error'))
|
||||
fromHex = float.fromhex
|
||||
def identical(x, y):
|
||||
if math.isnan(x) or math.isnan(y):
|
||||
if math.isnan(x) == math.isnan(y):
|
||||
return
|
||||
elif x == y and (x != 0.0 or math.copysign(1.0, x) == math.copysign(1.0, y)):
|
||||
return
|
||||
raise SyntaxError(f"{x} not identical to {y}")
|
||||
|
||||
invalid_inputs = [
|
||||
"infi", # misspelt infinities and nans
|
||||
"-Infinit",
|
||||
"++inf",
|
||||
"-+Inf",
|
||||
"--nan",
|
||||
"+-NaN",
|
||||
"snan",
|
||||
"NaNs",
|
||||
"nna",
|
||||
"an",
|
||||
"nf",
|
||||
"nfinity",
|
||||
"inity",
|
||||
"iinity",
|
||||
"0xnan",
|
||||
"",
|
||||
" ",
|
||||
"x1.0p0",
|
||||
"0xX1.0p0",
|
||||
"+ 0x1.0p0", # internal whitespace
|
||||
"- 0x1.0p0",
|
||||
"0 x1.0p0",
|
||||
"0x 1.0p0",
|
||||
"0x1 2.0p0",
|
||||
"+0x1 .0p0",
|
||||
"0x1. 0p0",
|
||||
"-0x1.0 1p0",
|
||||
"-0x1.0 p0",
|
||||
"+0x1.0p +0",
|
||||
"0x1.0p -0",
|
||||
"0x1.0p 0",
|
||||
"+0x1.0p+ 0",
|
||||
"-0x1.0p- 0",
|
||||
"++0x1.0p-0", # double signs
|
||||
"--0x1.0p0",
|
||||
"+-0x1.0p+0",
|
||||
"-+0x1.0p0",
|
||||
"0x1.0p++0",
|
||||
"+0x1.0p+-0",
|
||||
"-0x1.0p-+0",
|
||||
"0x1.0p--0",
|
||||
"0x1.0.p0",
|
||||
"0x.p0", # no hex digits before or after point
|
||||
"0x1,p0", # wrong decimal point character
|
||||
"0x1pa",
|
||||
"0x1p\uff10", # fullwidth Unicode digits
|
||||
"\uff10x1p0",
|
||||
"0x\uff11p0",
|
||||
"0x1.\uff10p0",
|
||||
"0x1p0 \n 0x2p0",
|
||||
"0x1p0\0 0x1p0", # embedded null byte is not end of string
|
||||
]
|
||||
|
||||
for x in invalid_inputs:
|
||||
assert_raises(ValueError, lambda: fromHex(x))
|
||||
|
||||
value_pairs = [
|
||||
("inf", INF),
|
||||
("-Infinity", -INF),
|
||||
("NaN", NAN),
|
||||
("1.0", 1.0),
|
||||
("-0x.2", -0.125),
|
||||
("-0.0", -0.0),
|
||||
]
|
||||
whitespace = [
|
||||
"",
|
||||
" ",
|
||||
"\t",
|
||||
"\n",
|
||||
"\n \t",
|
||||
"\f",
|
||||
"\v",
|
||||
"\r"
|
||||
]
|
||||
|
||||
for inp, expected in value_pairs:
|
||||
for lead in whitespace:
|
||||
for trail in whitespace:
|
||||
got = fromHex(lead + inp + trail)
|
||||
identical(got, expected)
|
||||
|
||||
MAX = fromHex('0x.fffffffffffff8p+1024') # max normal
|
||||
MIN = fromHex('0x1p-1022') # min normal
|
||||
TINY = fromHex('0x0.0000000000001p-1022') # min subnormal
|
||||
EPS = fromHex('0x0.0000000000001p0') # diff between 1.0 and next float up
|
||||
|
||||
# two spellings of infinity, with optional signs; case-insensitive
|
||||
identical(fromHex('inf'), INF)
|
||||
identical(fromHex('+Inf'), INF)
|
||||
identical(fromHex('-INF'), -INF)
|
||||
identical(fromHex('iNf'), INF)
|
||||
identical(fromHex('Infinity'), INF)
|
||||
identical(fromHex('+INFINITY'), INF)
|
||||
identical(fromHex('-infinity'), -INF)
|
||||
identical(fromHex('-iNFiNitY'), -INF)
|
||||
|
||||
# nans with optional sign; case insensitive
|
||||
identical(fromHex('nan'), NAN)
|
||||
identical(fromHex('+NaN'), NAN)
|
||||
identical(fromHex('-NaN'), NAN)
|
||||
identical(fromHex('-nAN'), NAN)
|
||||
|
||||
# variations in input format
|
||||
identical(fromHex('1'), 1.0)
|
||||
identical(fromHex('+1'), 1.0)
|
||||
identical(fromHex('1.'), 1.0)
|
||||
identical(fromHex('1.0'), 1.0)
|
||||
identical(fromHex('1.0p0'), 1.0)
|
||||
identical(fromHex('01'), 1.0)
|
||||
identical(fromHex('01.'), 1.0)
|
||||
identical(fromHex('0x1'), 1.0)
|
||||
identical(fromHex('0x1.'), 1.0)
|
||||
identical(fromHex('0x1.0'), 1.0)
|
||||
identical(fromHex('+0x1.0'), 1.0)
|
||||
identical(fromHex('0x1p0'), 1.0)
|
||||
identical(fromHex('0X1p0'), 1.0)
|
||||
identical(fromHex('0X1P0'), 1.0)
|
||||
identical(fromHex('0x1P0'), 1.0)
|
||||
identical(fromHex('0x1.p0'), 1.0)
|
||||
identical(fromHex('0x1.0p0'), 1.0)
|
||||
identical(fromHex('0x.1p4'), 1.0)
|
||||
identical(fromHex('0x.1p04'), 1.0)
|
||||
identical(fromHex('0x.1p004'), 1.0)
|
||||
identical(fromHex('0x1p+0'), 1.0)
|
||||
identical(fromHex('0x1P-0'), 1.0)
|
||||
identical(fromHex('+0x1p0'), 1.0)
|
||||
identical(fromHex('0x01p0'), 1.0)
|
||||
identical(fromHex('0x1p00'), 1.0)
|
||||
identical(fromHex(' 0x1p0 '), 1.0)
|
||||
identical(fromHex('\n 0x1p0'), 1.0)
|
||||
identical(fromHex('0x1p0 \t'), 1.0)
|
||||
identical(fromHex('0xap0'), 10.0)
|
||||
identical(fromHex('0xAp0'), 10.0)
|
||||
identical(fromHex('0xaP0'), 10.0)
|
||||
identical(fromHex('0xAP0'), 10.0)
|
||||
identical(fromHex('0xbep0'), 190.0)
|
||||
identical(fromHex('0xBep0'), 190.0)
|
||||
identical(fromHex('0xbEp0'), 190.0)
|
||||
identical(fromHex('0XBE0P-4'), 190.0)
|
||||
identical(fromHex('0xBEp0'), 190.0)
|
||||
identical(fromHex('0xB.Ep4'), 190.0)
|
||||
identical(fromHex('0x.BEp8'), 190.0)
|
||||
identical(fromHex('0x.0BEp12'), 190.0)
|
||||
|
||||
# moving the point around
|
||||
pi = fromHex('0x1.921fb54442d18p1')
|
||||
identical(fromHex('0x.006487ed5110b46p11'), pi)
|
||||
identical(fromHex('0x.00c90fdaa22168cp10'), pi)
|
||||
identical(fromHex('0x.01921fb54442d18p9'), pi)
|
||||
identical(fromHex('0x.03243f6a8885a3p8'), pi)
|
||||
identical(fromHex('0x.06487ed5110b46p7'), pi)
|
||||
identical(fromHex('0x.0c90fdaa22168cp6'), pi)
|
||||
identical(fromHex('0x.1921fb54442d18p5'), pi)
|
||||
identical(fromHex('0x.3243f6a8885a3p4'), pi)
|
||||
identical(fromHex('0x.6487ed5110b46p3'), pi)
|
||||
identical(fromHex('0x.c90fdaa22168cp2'), pi)
|
||||
identical(fromHex('0x1.921fb54442d18p1'), pi)
|
||||
identical(fromHex('0x3.243f6a8885a3p0'), pi)
|
||||
identical(fromHex('0x6.487ed5110b46p-1'), pi)
|
||||
identical(fromHex('0xc.90fdaa22168cp-2'), pi)
|
||||
identical(fromHex('0x19.21fb54442d18p-3'), pi)
|
||||
identical(fromHex('0x32.43f6a8885a3p-4'), pi)
|
||||
identical(fromHex('0x64.87ed5110b46p-5'), pi)
|
||||
identical(fromHex('0xc9.0fdaa22168cp-6'), pi)
|
||||
identical(fromHex('0x192.1fb54442d18p-7'), pi)
|
||||
identical(fromHex('0x324.3f6a8885a3p-8'), pi)
|
||||
identical(fromHex('0x648.7ed5110b46p-9'), pi)
|
||||
identical(fromHex('0xc90.fdaa22168cp-10'), pi)
|
||||
identical(fromHex('0x1921.fb54442d18p-11'), pi)
|
||||
identical(fromHex('0x1921fb54442d1.8p-47'), pi)
|
||||
identical(fromHex('0x3243f6a8885a3p-48'), pi)
|
||||
identical(fromHex('0x6487ed5110b46p-49'), pi)
|
||||
identical(fromHex('0xc90fdaa22168cp-50'), pi)
|
||||
identical(fromHex('0x1921fb54442d18p-51'), pi)
|
||||
identical(fromHex('0x3243f6a8885a30p-52'), pi)
|
||||
identical(fromHex('0x6487ed5110b460p-53'), pi)
|
||||
identical(fromHex('0xc90fdaa22168c0p-54'), pi)
|
||||
identical(fromHex('0x1921fb54442d180p-55'), pi)
|
||||
|
||||
assert (0.0).hex() == '0x0.0p+0'
|
||||
assert (-0.0).hex() == '-0x0.0p+0'
|
||||
@@ -266,10 +443,10 @@ assert float('nan').hex() == 'nan'
|
||||
|
||||
#for _ in range(10000):
|
||||
# f = random.random() * random.randint(0, 0x10000000000000000)
|
||||
# assert f == float.fromhex(f.hex())
|
||||
# assert f == fromHex(f.hex())
|
||||
|
||||
# Test float exponent:
|
||||
assert 1 if 1else 0 == 1
|
||||
assert 1 if 1 else 0 == 1
|
||||
|
||||
a = 3.
|
||||
assert a.__eq__(3) is True
|
||||
|
||||
+39
-13
@@ -583,30 +583,56 @@ impl PyFloat {
|
||||
|
||||
#[pymethod]
|
||||
fn fromhex(repr: PyStringRef, vm: &VirtualMachine) -> PyResult<f64> {
|
||||
hexf_parse::parse_hexf64(repr.as_str(), false)
|
||||
.or_else(|_| repr.as_str().parse::<f64>())
|
||||
.or_else(|_| match repr.as_str() {
|
||||
hexf_parse::parse_hexf64(repr.as_str().trim(), false).or_else(|_| {
|
||||
match repr.as_str().to_lowercase().trim() {
|
||||
"nan" => Ok(std::f64::NAN),
|
||||
"+nan" => Ok(std::f64::NAN),
|
||||
"-nan" => Ok(std::f64::NAN),
|
||||
"inf" => Ok(std::f64::INFINITY),
|
||||
"infinity" => Ok(std::f64::INFINITY),
|
||||
"+inf" => Ok(std::f64::INFINITY),
|
||||
"+infinity" => Ok(std::f64::INFINITY),
|
||||
"-inf" => Ok(std::f64::NEG_INFINITY),
|
||||
"-infinity" => Ok(std::f64::NEG_INFINITY),
|
||||
value => {
|
||||
let mut hex = String::new();
|
||||
if value.contains("0x") {
|
||||
for ch in value.chars() {
|
||||
if ch == 'p' {
|
||||
hex.push_str(".p");
|
||||
} else {
|
||||
hex.push(ch);
|
||||
}
|
||||
let has_0x = value.contains("0x");
|
||||
let has_p = value.contains('p');
|
||||
let has_dot = value.contains('.');
|
||||
let mut start = 0;
|
||||
|
||||
if !has_0x && value.starts_with('-') {
|
||||
hex.push_str("-0x");
|
||||
start += 1;
|
||||
} else if !has_0x {
|
||||
hex.push_str("0x");
|
||||
if value.starts_with('+') {
|
||||
start += 1;
|
||||
}
|
||||
} else {
|
||||
hex = value.to_string();
|
||||
}
|
||||
|
||||
for (index, ch) in value.chars().enumerate() {
|
||||
if ch == 'p' && has_dot {
|
||||
hex.push_str("p");
|
||||
} else if ch == 'p' && !has_dot {
|
||||
hex.push_str(".p");
|
||||
} else if index >= start {
|
||||
hex.push(ch);
|
||||
}
|
||||
}
|
||||
|
||||
if !has_p && has_dot {
|
||||
hex.push_str("p0");
|
||||
} else if !has_p && !has_dot {
|
||||
hex.push_str(".p0")
|
||||
}
|
||||
|
||||
hexf_parse::parse_hexf64(hex.as_str(), false).map_err(|_| {
|
||||
vm.new_value_error("invalid hexadecimal floating-point string".to_string())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
|
||||
Reference in new issue
Block a user