Skip to content

Commit

Permalink
Merge pull request #12 from KlausC/krc/infnan
Browse files Browse the repository at this point in the history
recognize `infinity` and `nan` correctly
  • Loading branch information
KlausC committed Jul 22, 2022
2 parents 7063bc6 + 70dfa6a commit 0dca73d
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 33 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ jobs:
- 'nightly'
os:
- ubuntu-latest
- windows-latest
- macos-latest
arch:
- x64
steps:
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Scanf"
uuid = "6ef1bc8b-493b-44e1-8d40-549aa65c4b41"
authors = ["KlausC <[email protected]> and contributors"]
version = "0.5.2"
version = "0.5.3"

[compat]
julia = "1.5"
Expand Down
66 changes: 43 additions & 23 deletions src/Scanf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -434,17 +434,18 @@ end
width = ifelse(width == 0, typemax(Int), width)
digits = DECIMAL
expch = b"eEfF"
x_sign = 0x01
x_sep = 0x02
x_exp = 0x04
x_base = 0x08
x_inexp = 0x10
x_nexp = 0x20
x_mdigits = 0x40
x_edigits = 0x80
x_sign = 0x001
x_sep = 0x002
x_exp = 0x004
x_base = 0x008
x_inexp = 0x010
x_nexp = 0x020
x_mdigits = 0x040
x_edigits = 0x080
x_infnan = 0x100
l = 0
out = IOBuffer()
status = x_sign | x_sep | x_base | x_edigits
status = x_sign | x_sep | x_base | x_edigits | x_infnan
while l < width && !eof(io)
b = peek(io)
if status & x_base != 0 && b == UInt8('0')
Expand All @@ -456,27 +457,27 @@ end
else
status |= x_mdigits
end
status = (status | x_exp | x_edigits) & ~(x_base | x_sign)
status = (status | x_exp | x_edigits) & ~(x_base | x_sign | x_infnan)
continue
elseif b in digits
status = (status | x_exp) & ~(x_base | x_sign)
status = (status | x_exp) & ~(x_base | x_sign | x_infnan)
status |= (status & x_inexp != 0) ? x_edigits : x_mdigits
elseif b == UInt8('-') && (status & x_sign) != 0
status |= ifelse(status & x_base == 0, x_nexp, 0)
status = status & ~x_sign
elseif b == UInt8('+') && (status & x_sign) != 0
status = status & ~x_sign
elseif b == UInt8('.') && (status & x_sep) != 0
status = status & ~(x_base | x_sign | x_sep)
status = status & ~(x_base | x_sign | x_sep | x_infnan)
elseif b in expch && (status & x_exp) != 0
status = (status & ~(x_base | x_exp | x_edigits | x_sep)) | x_sign | x_inexp
digits = DECIMAL
elseif b in b"iI"
elseif b in b"iI" && (status & x_infnan ) != 0
n = expect(io, out, b"INFINITY", 3)
l = (n == 3 || n == 8) ? l + n : 0
status |= x_mdigits | x_edigits
break
elseif b in b"nN"
elseif b in b"nN" && (status & x_infnan ) != 0
n = expect(io, out, b"NAN", 3)
l = n == 3 ? l + n : 0
status |= x_mdigits | x_edigits
Expand All @@ -492,18 +493,37 @@ end

# helpers for matching nan and inf
@inline function expect(io::IO, out::IO, bytes::AbstractVector{UInt8}, n)
l = 1
while l <= length(bytes) && !eof(io)
b = peek(io)
if b == bytes[l] || b == bytes[l] + (UInt8('a') - UInt8('A'))
l <= n && write(out, b)
skip(io, 1)
l += 1
mark(io)
m = length(bytes)
rbytes = read(io, n)
if !isequiv(rbytes, bytes, n)
reset(io)
return 0
end
write(out, rbytes)
if n < m
mark(io)
xbytes = read(io, m - n)
append!(rbytes, xbytes)
if isequiv(rbytes, bytes, m)
unmark(io)
return m
else
break
reset(io)
return n
end
end
return n
end

@inline function isequiv(rbytes, bytes, n)
length(rbytes) >= n || return false
for i = 1:n
if !( rbytes[i] == bytes[i] || rbytes[i] == bytes[i] + (UInt8('a') - UInt8('A')) )
return false
end
end
return l - 1
return true
end

# position counter spec
Expand Down
24 changes: 15 additions & 9 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,21 @@ using Test, Scanf
# floating point nan and infinity
@testset "float to $T" for T in (Float64, Float32, Float16, BigFloat)
f = Scanf.format"%f%n"
@testset "$inp" for (inp, res, rr, nn) in (
("InfX", T(Inf), 2, 3),
("-InFX", T(-Inf), 2, 4),
("infiNITYX", T(Inf), 2, 8),
("NANX", T(NaN), 2, 3),
("-nanX", T(-NaN), 2, 4),
("infiX", T(0.0), 0, 0),
("naX", T(0.0), 0, 0),
@testset "$inp" for (inp, res, rr, nn, nc) in (
("InfX", T(Inf), 2, 3, "X"),
("-InFX", T(-Inf), 2, 4, "X"),
("infiNITYX", T(Inf), 2, 8, "X"),
("NANX", T(NaN), 2, 3, "X"),
("-nanX", T(-NaN), 2, 4, "X"),
("infiX", T(Inf), 2, 3, "iX"),
("naX", T(0.0), 0, 0, "naX"),
)
io = IOBuffer(inp)
r, x, n = scanf(io, f, T(0.0), 0)
@test r == rr
@test n == nn
@test x === res || T <: BigFloat && (x == res || isnan(x) && isnan(res))
@test peek(io, Char) == 'X'
@test peek(io, String) == nc
end
end

Expand Down Expand Up @@ -341,4 +341,10 @@ using Test, Scanf
@test scanf("äbcd", f, ['1']) == (1, ['ä'])
end

@testset "issue #11" begin
f = Scanf.format"%f%fi%n"
@test scanf("1.5-2.1i", f, 0.0, 0.0, 0) == (3, 1.5, -2.1, 8)
@test isequal(scanf("-NaN -Infi", f, 0.0, 0.0, 0), (3, NaN, -Inf, 10))
end

end # @testset "Scanf"

0 comments on commit 0dca73d

Please sign in to comment.