Skip to content

Commit

Permalink
add specs
Browse files Browse the repository at this point in the history
  • Loading branch information
HertzDevil committed Nov 18, 2024
1 parent 83276b0 commit e5909bb
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 6 deletions.
103 changes: 103 additions & 0 deletions spec/manual/string_to_f_supplemental_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Runs the fast_float supplemental test suite:
# https://github.com/fastfloat/supplemental_test_files
#
# Supplemental data files for testing floating parsing (credit: Nigel Tao for
# the data)
#
# LICENSE file (Apache 2): https://github.com/nigeltao/parse-number-fxx-test-data/blob/main/LICENSE
#
# Due to the sheer volume of the test cases (5.2+ million test cases across
# 270+ MB of text) these specs are not vendored into the Crystal repository.

require "spec"
require "http/client"
require "../support/number"
require "wait_group"

# these specs permit underflow and overflow to return 0 and infinity
# respectively (when `ret.rc == Errno::ERANGE`), so we have to use
# `Float::FastFloat` directly
def fast_float_to_f32(str)
value = uninitialized Float32
start = str.to_unsafe
finish = start + str.bytesize
options = Float::FastFloat::ParseOptionsT(typeof(str.to_unsafe.value)).new(format: :general)

ret = Float::FastFloat::BinaryFormat_Float32.new.from_chars_advanced(start, finish, pointerof(value), options)
{Errno::NONE, Errno::ERANGE}.should contain(ret.ec)
value
end

def fast_float_to_f64(str)
value = uninitialized Float64
start = str.to_unsafe
finish = start + str.bytesize
options = Float::FastFloat::ParseOptionsT(typeof(str.to_unsafe.value)).new(format: :general)

ret = Float::FastFloat::BinaryFormat_Float64.new.from_chars_advanced(start, finish, pointerof(value), options)
{Errno::NONE, Errno::ERANGE}.should contain(ret.ec)
value
end

RAW_BASE_URL = "https://raw.githubusercontent.com/fastfloat/supplemental_test_files/7cc512a7c60361ebe1baf54991d7905efdc62aa0/data/" # @1.0.0

TEST_SUITES = %w(
freetype-2-7.txt
google-double-conversion.txt
google-wuffs.txt
ibm-fpgen.txt
lemire-fast-double-parser.txt
lemire-fast-float.txt
more-test-cases.txt
remyoudompheng-fptest-0.txt
remyoudompheng-fptest-1.txt
remyoudompheng-fptest-2.txt
remyoudompheng-fptest-3.txt
tencent-rapidjson.txt
ulfjack-ryu.txt
)

test_suite_cache = {} of String => Array({UInt32, UInt64, String})
puts "Fetching #{TEST_SUITES.size} test suites"
WaitGroup.wait do |wg|
TEST_SUITES.each do |suite|
wg.spawn do
url = RAW_BASE_URL + suite

cache = HTTP::Client.get(url) do |res|
res.body_io.each_line.map do |line|
args = line.split(' ')
raise "BUG: should have 4 args" unless args.size == 4

# f16_bits = args[0].to_u16(16)
f32_bits = args[1].to_u32(16)
f64_bits = args[2].to_u64(16)
str = args[3]

{f32_bits, f64_bits, str}
end.to_a
end

puts "#{cache.size} test cases cached from #{url}"
test_suite_cache[suite] = cache
end
end
end
puts "There are a total of #{test_suite_cache.sum(&.last.size)} test cases"

describe String do
describe "#to_f" do
test_suite_cache.each do |suite, cache|
describe suite do
each_hardware_rounding_mode do |mode, mode_name|
it mode_name do
cache.each do |f32_bits, f64_bits, str|
fast_float_to_f32(str).unsafe_as(UInt32).should eq(f32_bits)
fast_float_to_f64(str).unsafe_as(UInt64).should eq(f64_bits)
end
end
end
end
end
end
end
5 changes: 0 additions & 5 deletions spec/std/string/fast_float_spec.cr

This file was deleted.

2 changes: 1 addition & 1 deletion spec/std/string_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ describe "String" do
it { "1Y2P0IJ32E8E7".to_i64(36).should eq(9223372036854775807) }
end

# more specs are available in `spec/std/string/fast_float_spec.cr`
# more specs are available in `spec/manual/string_to_f_supplemental_spec.cr`
it "does to_f" do
expect_raises(ArgumentError) { "".to_f }
"".to_f?.should be_nil
Expand Down
32 changes: 32 additions & 0 deletions spec/support/number.cr
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,35 @@ macro hexfloat(str)
::Float64.parse_hexfloat({{ str }})
{% end %}
end

# See also: https://github.com/crystal-lang/crystal/issues/15192
lib LibC
{% if flag?(:win32) %}
FE_TONEAREST = 0x00000000
FE_DOWNWARD = 0x00000100
FE_UPWARD = 0x00000200
FE_TOWARDZERO = 0x00000300
{% else %}
FE_TONEAREST = 0x00000000
FE_DOWNWARD = 0x00000400
FE_UPWARD = 0x00000800
FE_TOWARDZERO = 0x00000C00
{% end %}

fun fegetround : Int
fun fesetround(round : Int) : Int
end

def with_hardware_rounding_mode(mode, &)
old_mode = LibC.fegetround
LibC.fesetround(mode)
yield ensure LibC.fesetround(old_mode)
end

def each_hardware_rounding_mode(&)
{% for mode in %w(FE_TONEAREST FE_DOWNWARD FE_UPWARD FE_TOWARDZERO) %}
with_hardware_rounding_mode(LibC::{{ mode.id }}) do
yield LibC::{{ mode.id }}, {{ mode }}
end
{% end %}
end

0 comments on commit e5909bb

Please sign in to comment.