require "./spec_helper" require "../support/number" require "spec/helpers/string" require "big" # use same name for `sprintf` and `IO#printf` so that `assert_prints` can be leveraged private def fprintf(format, *args) sprintf(format, *args) end private def fprintf(io : IO, format, *args) io.printf(format, *args) end private def assert_sprintf(format, args, result, *, file = __FILE__, line = __LINE__) assert_prints fprintf(format, args), result, file: file, line: line end describe "::sprintf" do it "works" do assert_sprintf "Hello %d world", 123, "Hello 123 world" assert_sprintf "Hello %d world", [123], "Hello 123 world" assert_sprintf "foo %d bar %s baz %d goo", [1, "hello", 2], "foo 1 bar hello baz 2 goo" end it "accepts multiple positional arguments" do assert_prints fprintf("%d %d %d", 1, 23, 456), "1 23 456" assert_prints fprintf("%*.*d,%*s", 10, 6, 123, 10, "foo"), " 000123, foo" assert_prints fprintf("foo"), "foo" end it "doesn't format %%" do assert_sprintf "%%%d", 1, "%1" assert_sprintf "%%*%%", [1, 2, 3], "%*%" end it "doesn't accept modifiers for %%" do expect_raises(ArgumentError) { sprintf("%0%") } expect_raises(ArgumentError) { sprintf("%+%") } expect_raises(ArgumentError) { sprintf("%-%") } expect_raises(ArgumentError) { sprintf("% %") } expect_raises(ArgumentError) { sprintf("%#%") } expect_raises(ArgumentError) { sprintf("%.0%") } expect_raises(ArgumentError) { sprintf("%*%", 1) } expect_raises(ArgumentError) { sprintf("%0%") } expect_raises(ArgumentError) { sprintf("%+%") } expect_raises(ArgumentError) { sprintf("%-%") } expect_raises(ArgumentError) { sprintf("% %") } expect_raises(ArgumentError) { sprintf("%#%") } expect_raises(ArgumentError) { sprintf("%.0%") } expect_raises(ArgumentError) { sprintf("%*%", 1) } end context "integers" do context "base specifier" do it "supports base 2" do assert_sprintf "%b", 123, "1111011" assert_sprintf "%+b", 123, "+1111011" assert_sprintf "% b", 123, " 1111011" assert_sprintf "%-b", 123, "1111011" assert_sprintf "%10b", 123, " 1111011" assert_sprintf "%-10b", 123, "1111011 " end it "supports base 8" do assert_sprintf "%o", 123, "173" assert_sprintf "%+o", 123, "+173" assert_sprintf "% o", 123, " 173" assert_sprintf "%-o", 123, "173" assert_sprintf "%6o", 123, " 173" assert_sprintf "%-6o", 123, "173 " end it "supports base 10" do assert_sprintf "%d", 123, "123" assert_sprintf "%+d", 123, "+123" assert_sprintf "% d", 123, " 123" assert_sprintf "%-d", 123, "123" assert_sprintf "%6d", 123, " 123" assert_sprintf "%-6d", 123, "123 " assert_sprintf "%i", 123, "123" assert_sprintf "%+i", 123, "+123" assert_sprintf "% i", 123, " 123" assert_sprintf "%-i", 123, "123" assert_sprintf "%6i", 123, " 123" assert_sprintf "%-6i", 123, "123 " end it "supports base 16" do assert_sprintf "%x", 123, "7b" assert_sprintf "%+x", 123, "+7b" assert_sprintf "% x", 123, " 7b" assert_sprintf "%-x", 123, "7b" assert_sprintf "%6x", 123, " 7b" assert_sprintf "%-6x", 123, "7b " assert_sprintf "%X", 123, "7B" assert_sprintf "%+X", 123, "+7B" assert_sprintf "% X", 123, " 7B" assert_sprintf "%-X", 123, "7B" assert_sprintf "%6X", 123, " 7B" assert_sprintf "%-6X", 123, "7B " assert_sprintf "こんに%xちは", 123, "こんに7bちは" assert_sprintf "こんに%Xちは", 123, "こんに7Bちは" end end context "width specifier" do it "sets the minimum length of the string" do assert_sprintf "%20d", 123, " 123" assert_sprintf "%20d", -123, " -123" assert_sprintf "%20d", 0, " 0" assert_sprintf "%4d", 123, " 123" assert_sprintf "%4d", -123, "-123" assert_sprintf "%4d", 0, " 0" assert_sprintf "%2d", 123, "123" assert_sprintf "%2d", -123, "-123" assert_sprintf "%2d", 0, " 0" assert_sprintf "%*d", [20, 123], " 123" assert_sprintf "%*d", [20, -123], " -123" assert_sprintf "%*d", [20, 0], " 0" assert_sprintf "%*d", [0, 123], "123" assert_sprintf "%*d", [0, -123], "-123" assert_sprintf "%*d", [0, 0], "0" end it "left-justifies on negative width" do assert_sprintf "%*d", [-20, 123], "123 " assert_sprintf "%*d", [-20, -123], "-123 " assert_sprintf "%*d", [-20, 0], "0 " assert_sprintf "%*d", [-4, 123], "123 " assert_sprintf "%*d", [-4, -123], "-123" assert_sprintf "%*d", [-4, 0], "0 " assert_sprintf "%*d", [-2, 123], "123" assert_sprintf "%*d", [-2, -123], "-123" assert_sprintf "%*d", [-2, 0], "0 " assert_sprintf "%-*d", [-20, 123], "123 " assert_sprintf "%-*d", [-20, -123], "-123 " assert_sprintf "%-*d", [-20, 0], "0 " end end context "precision specifier" do it "sets the minimum length of the number part" do assert_sprintf "%.12d", 123, "000000000123" assert_sprintf "%.12d", -123, "-000000000123" assert_sprintf "%.12d", 0, "000000000000" assert_sprintf "%.4d", 123, "0123" assert_sprintf "%.4d", -123, "-0123" assert_sprintf "%.4d", 0, "0000" assert_sprintf "%.2d", 123, "123" assert_sprintf "%.2d", -123, "-123" assert_sprintf "%.2d", 0, "00" assert_sprintf "%.0d", 123, "123" assert_sprintf "%.0d", -123, "-123" assert_sprintf "%.0d", 0, "" end it "can be used with width" do assert_sprintf "%20.12d", 123, " 000000000123" assert_sprintf "%20.12d", -123, " -000000000123" assert_sprintf "%20.12d", 0, " 000000000000" assert_sprintf "%-20.12d", 123, "000000000123 " assert_sprintf "%-20.12d", -123, "-000000000123 " assert_sprintf "%-20.12d", 0, "000000000000 " assert_sprintf "%8.12d", 123, "000000000123" assert_sprintf "%8.12d", -123, "-000000000123" assert_sprintf "%8.12d", 0, "000000000000" assert_sprintf "%+13.12d", 123, "+000000000123" assert_sprintf "%+13.12d", -123, "-000000000123" assert_sprintf "%+13.12d", 0, "+000000000000" assert_sprintf "%- 20.12d", 123, " 000000000123 " assert_sprintf "%- 20.12d", -123, "-000000000123 " assert_sprintf "%- 20.12d", 0, " 000000000000 " assert_sprintf "%*.*d", [20, 12, 123], " 000000000123" assert_sprintf "%*.*d", [20, 12, -123], " -000000000123" assert_sprintf "%*.*d", [20, 12, 0], " 000000000000" assert_sprintf "%*.*d", [-20, -12, 123], "123 " assert_sprintf "%*.*d", [-20, -12, -123], "-123 " assert_sprintf "%*.*d", [-20, -12, 0], "0 " end it "is ignored if precision argument is negative" do assert_sprintf "%.*d", [-2, 123], "123" assert_sprintf "%.*d", [-2, -123], "-123" assert_sprintf "%.*d", [-2, 0], "0" assert_sprintf "%020.*d", [-2, 123], "00000000000000000123" assert_sprintf "%020.*d", [-2, -123], "-0000000000000000123" assert_sprintf "%020.*d", [-2, 0], "00000000000000000000" end end context "sharp flag" do it "adds a base prefix" do assert_sprintf "%#b", 123, "0b1111011" assert_sprintf "%#o", 123, "0o173" assert_sprintf "%#x", 123, "0x7b" assert_sprintf "%#X", 123, "0X7B" assert_sprintf "%#b", -123, "-0b1111011" assert_sprintf "%#o", -123, "-0o173" assert_sprintf "%#x", -123, "-0x7b" assert_sprintf "%#X", -123, "-0X7B" end it "omits the base prefix for 0" do assert_sprintf "%#b", 0, "0" assert_sprintf "%#o", 0, "0" assert_sprintf "%#x", 0, "0" assert_sprintf "%#X", 0, "0" end end context "plus flag" do it "writes a plus sign for positive integers" do assert_sprintf "%+d", 123, "+123" assert_sprintf "%+d", -123, "-123" assert_sprintf "%+d", 0, "+0" end it "writes plus sign after left space-padding" do assert_sprintf "%+20d", 123, " +123" assert_sprintf "%+20d", -123, " -123" assert_sprintf "%+20d", 0, " +0" end it "writes plus sign before left zero-padding" do assert_sprintf "%+020d", 123, "+0000000000000000123" assert_sprintf "%+020d", -123, "-0000000000000000123" assert_sprintf "%+020d", 0, "+0000000000000000000" end end context "space flag" do it "writes a space for positive integers" do assert_sprintf "% d", 123, " 123" assert_sprintf "% d", -123, "-123" assert_sprintf "% d", 0, " 0" end it "writes space before left padding" do assert_sprintf "% 20d", 123, " 123" assert_sprintf "% 20d", -123, " -123" assert_sprintf "% 20d", 0, " 0" assert_sprintf "% 020d", 123, " 0000000000000000123" assert_sprintf "% 020d", -123, "-0000000000000000123" assert_sprintf "% 020d", 0, " 0000000000000000000" end it "is ignored if plus flag is also specified" do assert_sprintf "%+ d", 123, "+123" assert_sprintf "% +d", 123, "+123" assert_sprintf "%+ 20d", 123, " +123" assert_sprintf "% +20d", 123, " +123" assert_sprintf "%+ 020d", 123, "+0000000000000000123" assert_sprintf "% +020d", 123, "+0000000000000000123" end end context "zero flag" do it "left-pads the result with zeros" do assert_sprintf "%020d", 123, "00000000000000000123" assert_sprintf "%020d", -123, "-0000000000000000123" assert_sprintf "%020d", 0, "00000000000000000000" assert_sprintf "%+020d", 123, "+0000000000000000123" assert_sprintf "%+020d", -123, "-0000000000000000123" assert_sprintf "%+020d", 0, "+0000000000000000000" assert_sprintf "% 020d", 123, " 0000000000000000123" assert_sprintf "% 020d", -123, "-0000000000000000123" assert_sprintf "% 020d", 0, " 0000000000000000000" end it "is ignored if string is left-justified" do assert_sprintf "%-020d", 123, "123 " assert_sprintf "%-020d", -123, "-123 " assert_sprintf "%-020d", 0, "0 " assert_sprintf "%0-20d", 123, "123 " assert_sprintf "%0-20d", -123, "-123 " assert_sprintf "%0-20d", 0, "0 " assert_sprintf "%0*d", [-20, 123], "123 " assert_sprintf "%0*d", [-20, -123], "-123 " assert_sprintf "%0*d", [-20, 0], "0 " assert_sprintf "%-0*d", [-20, 123], "123 " assert_sprintf "%-0*d", [-20, -123], "-123 " assert_sprintf "%-0*d", [-20, 0], "0 " end it "is ignored if precision is specified" do assert_sprintf "%020.12d", 123, " 000000000123" assert_sprintf "%020.12d", -123, " -000000000123" assert_sprintf "%020.12d", 0, " 000000000000" assert_sprintf "%020.*d", [12, 123], " 000000000123" assert_sprintf "%020.*d", [12, -123], " -000000000123" assert_sprintf "%020.*d", [12, 0], " 000000000000" assert_sprintf "%020.*d", [-12, 123], "00000000000000000123" assert_sprintf "%020.*d", [-12, -123], "-0000000000000000123" assert_sprintf "%020.*d", [-12, 0], "00000000000000000000" end end context "minus flag" do it "left-justifies the string" do assert_sprintf "%-d", 123, "123" assert_sprintf "%-d", -123, "-123" assert_sprintf "%-d", 0, "0" assert_sprintf "%-20d", 123, "123 " assert_sprintf "%-20d", -123, "-123 " assert_sprintf "%-20d", 0, "0 " assert_sprintf "%-4d", 123, "123 " assert_sprintf "%-4d", -123, "-123" assert_sprintf "%-4d", 0, "0 " assert_sprintf "%-2d", 123, "123" assert_sprintf "%-2d", -123, "-123" assert_sprintf "%-2d", 0, "0 " end it "reserves space for the number prefix" do assert_sprintf "%-+20d", 123, "+123 " assert_sprintf "%-+20d", -123, "-123 " assert_sprintf "%-+20d", 0, "+0 " assert_sprintf "%- 20d", 123, " 123 " assert_sprintf "%- 20d", -123, "-123 " assert_sprintf "%- 20d", 0, " 0 " assert_sprintf "%-#20b", 123, "0b1111011 " assert_sprintf "%-#20b", -123, "-0b1111011 " assert_sprintf "%-#20b", 0, "0 " end end it "works with Int*::MIN" do assert_sprintf "%d", Int8::MIN, "-128" assert_sprintf "%d", Int16::MIN, "-32768" assert_sprintf "%d", Int32::MIN, "-2147483648" assert_sprintf "%d", Int64::MIN, "-9223372036854775808" end it "works with BigInt" do assert_sprintf "%d", 123.to_big_i, "123" assert_sprintf "%300.250d", 10.to_big_i ** 200, "#{" " * 50}#{"0" * 49}1#{"0" * 200}" assert_sprintf "%- #300.250X", 16.to_big_i ** 200 - 1, " 0X#{"0" * 50}#{"F" * 200}#{" " * 47}" end it "works with BigFloat" do assert_sprintf "%d", 123.to_big_f, "123" assert_sprintf "%80.70d", 2.to_big_i ** 200, " 0000000001606938044258990275541962092341162602522202993782792835301376" assert_sprintf "%- #70.60X", 2.to_big_f ** 200 - 2.to_big_f ** 120, " 0X0000000000FFFFFFFFFFFFFFFFFFFF000000000000000000000000000000 " end it "works with BigDecimal" do assert_sprintf "%d", 123.to_big_d, "123" assert_sprintf "%300.250d", 10.to_big_d ** 200, "#{" " * 50}#{"0" * 49}1#{"0" * 200}" assert_sprintf "%- #300.250X", 16.to_big_d ** 200 - 1, " 0X#{"0" * 50}#{"F" * 200}#{" " * 47}" end end it "doesn't stop at null character when doing '%'" do assert_sprintf "1\u{0}%i\u{0}3", 2, "1\u00002\u00003" end if String::Formatter::HAS_RYU_PRINTF describe "floats" do context "fixed format" do it "works" do assert_sprintf "%f", 123, "123.000000" assert_sprintf "%12f", 123.45, " 123.450000" assert_sprintf "%-12f", 123.45, "123.450000 " assert_sprintf "% f", 123.45, " 123.450000" assert_sprintf "%+f", 123, "+123.000000" assert_sprintf "%012f", 123, "00123.000000" assert_sprintf "%.f", 1234.56, "1235" assert_sprintf "%.2f", 1234.5678, "1234.57" assert_sprintf "%10.2f", 1234.5678, " 1234.57" assert_sprintf "%*.2f", [10, 1234.5678], " 1234.57" assert_sprintf "%*.*f", [10, 2, 1234.5678], " 1234.57" assert_sprintf "%.2f", 2.536_f32, "2.54" assert_sprintf "%+0*.*f", [10, 2, 2.536_f32], "+000002.54" assert_sprintf "%#.0f", 1234.56, "1235." assert_sprintf "%#.1f", 1234.56, "1234.6" expect_raises(ArgumentError, "Expected dynamic value '*' to be an Int - \"not a number\" (String)") do sprintf("%*f", ["not a number", 2.536_f32]) end assert_sprintf "%12.2f %12.2f %6.2f %.2f", [2.0, 3.0, 4.0, 5.0], " 2.00 3.00 4.00 5.00" assert_sprintf "%f", 1e15, "1000000000000000.000000" end end context "scientific format" do it "works" do assert_sprintf "%e", 123.45, "1.234500e+02" assert_sprintf "%E", 123.45, "1.234500E+02" assert_sprintf "%e", Float64::MAX, "1.797693e+308" assert_sprintf "%e", Float64::MIN_POSITIVE, "2.225074e-308" assert_sprintf "%e", Float64::MIN_SUBNORMAL, "4.940656e-324" assert_sprintf "%e", 0.0, "0.000000e+00" assert_sprintf "%e", -0.0, "-0.000000e+00" assert_sprintf "%e", -Float64::MIN_SUBNORMAL, "-4.940656e-324" assert_sprintf "%e", -Float64::MIN_POSITIVE, "-2.225074e-308" assert_sprintf "%e", Float64::MIN, "-1.797693e+308" end context "width specifier" do it "sets the minimum length of the string" do assert_sprintf "%20e", 123.45, " 1.234500e+02" assert_sprintf "%20e", -123.45, " -1.234500e+02" assert_sprintf "%+20e", 123.45, " +1.234500e+02" assert_sprintf "%13e", 123.45, " 1.234500e+02" assert_sprintf "%13e", -123.45, "-1.234500e+02" assert_sprintf "%+13e", 123.45, "+1.234500e+02" assert_sprintf "%12e", 123.45, "1.234500e+02" assert_sprintf "%12e", -123.45, "-1.234500e+02" assert_sprintf "%+12e", 123.45, "+1.234500e+02" assert_sprintf "%2e", 123.45, "1.234500e+02" assert_sprintf "%2e", -123.45, "-1.234500e+02" assert_sprintf "%+2e", 123.45, "+1.234500e+02" end it "left-justifies on negative width" do assert_sprintf "%*e", [-20, 123.45], "1.234500e+02 " end end context "precision specifier" do it "sets the minimum length of the fractional part" do assert_sprintf "%.0e", 2.0, "2e+00" assert_sprintf "%.0e", 2.5.prev_float, "2e+00" assert_sprintf "%.0e", 2.5, "2e+00" assert_sprintf "%.0e", 2.5.next_float, "3e+00" assert_sprintf "%.0e", 3.0, "3e+00" assert_sprintf "%.0e", 3.5.prev_float, "3e+00" assert_sprintf "%.0e", 3.5, "4e+00" assert_sprintf "%.0e", 3.5.next_float, "4e+00" assert_sprintf "%.0e", 4.0, "4e+00" assert_sprintf "%.0e", 9.5, "1e+01" assert_sprintf "%.100e", 1.1, "1.1000000000000000888178419700125232338905334472656250000000000000000000000000000000000000000000000000e+00" assert_sprintf "%.10000e", 1.0, "1.#{"0" * 10000}e+00" assert_sprintf "%.1000e", Float64::MIN_POSITIVE.prev_float, "2.2250738585072008890245868760858598876504231122409594654935248025624400092282356951" \ "787758888037591552642309780950434312085877387158357291821993020294379224223559819827" \ "501242041788969571311791082261043971979604000454897391938079198936081525613113376149" \ "842043271751033627391549782731594143828136275113838604094249464942286316695429105080" \ "201815926642134996606517803095075913058719846423906068637102005108723282784678843631" \ "944515866135041223479014792369585208321597621066375401613736583044193603714778355306" \ "682834535634005074073040135602968046375918583163124224521599262546494300836851861719" \ "422417646455137135420132217031370496583210154654068035397417906022589503023501937519" \ "773030945763173210852507299305089761582519159720757232455434770912461317493580281734" \ "466552734375000000000000000000000000000000000000000000000000000000000000000000000000" \ "000000000000000000000000000000000000000000000000000000000000000000000000000000000000" \ "000000000000000000000000000000000000000000000000000000000000000000000000000000e-308" end it "can be used with width" do assert_sprintf "%20.13e", 123.45, " 1.2345000000000e+02" assert_sprintf "%20.13e", -123.45, "-1.2345000000000e+02" assert_sprintf "%20.13e", 0.0, " 0.0000000000000e+00" assert_sprintf "%-20.13e", 123.45, "1.2345000000000e+02 " assert_sprintf "%-20.13e", -123.45, "-1.2345000000000e+02" assert_sprintf "%-20.13e", 0.0, "0.0000000000000e+00 " assert_sprintf "%8.13e", 123.45, "1.2345000000000e+02" assert_sprintf "%8.13e", -123.45, "-1.2345000000000e+02" assert_sprintf "%8.13e", 0.0, "0.0000000000000e+00" end it "is ignored if precision argument is negative" do assert_sprintf "%.*e", [-2, 123.45], "1.234500e+02" end end context "sharp flag" do it "prints a decimal point even if no digits follow" do assert_sprintf "%#.0e", 1.0, "1.e+00" assert_sprintf "%#.0e", 10000.0, "1.e+04" assert_sprintf "%#.0e", 1.0e+23, "1.e+23" assert_sprintf "%#.0e", 1.0e-100, "1.e-100" assert_sprintf "%#.0e", 0.0, "0.e+00" assert_sprintf "%#.0e", -0.0, "-0.e+00" end end context "plus flag" do it "writes a plus sign for positive values" do assert_sprintf "%+e", 123.45, "+1.234500e+02" assert_sprintf "%+e", -123.45, "-1.234500e+02" assert_sprintf "%+e", 0.0, "+0.000000e+00" end it "writes plus sign after left space-padding" do assert_sprintf "%+20e", 123.45, " +1.234500e+02" assert_sprintf "%+20e", -123.45, " -1.234500e+02" assert_sprintf "%+20e", 0.0, " +0.000000e+00" end it "writes plus sign before left zero-padding" do assert_sprintf "%+020e", 123.45, "+00000001.234500e+02" assert_sprintf "%+020e", -123.45, "-00000001.234500e+02" assert_sprintf "%+020e", 0.0, "+00000000.000000e+00" end end context "space flag" do it "writes a space for positive values" do assert_sprintf "% e", 123.45, " 1.234500e+02" assert_sprintf "% e", -123.45, "-1.234500e+02" assert_sprintf "% e", 0.0, " 0.000000e+00" end it "writes space before left space-padding" do assert_sprintf "% 20e", 123.45, " 1.234500e+02" assert_sprintf "% 20e", -123.45, " -1.234500e+02" assert_sprintf "% 20e", 0.0, " 0.000000e+00" assert_sprintf "% 020e", 123.45, " 00000001.234500e+02" assert_sprintf "% 020e", -123.45, "-00000001.234500e+02" assert_sprintf "% 020e", 0.0, " 00000000.000000e+00" end it "is ignored if plus flag is also specified" do assert_sprintf "% +e", 123.45, "+1.234500e+02" assert_sprintf "%+ e", -123.45, "-1.234500e+02" end end context "zero flag" do it "left-pads the result with zeros" do assert_sprintf "%020e", 123.45, "000000001.234500e+02" assert_sprintf "%020e", -123.45, "-00000001.234500e+02" assert_sprintf "%020e", 0.0, "000000000.000000e+00" end it "is ignored if string is left-justified" do assert_sprintf "%-020e", 123.45, "1.234500e+02 " assert_sprintf "%-020e", -123.45, "-1.234500e+02 " assert_sprintf "%-020e", 0.0, "0.000000e+00 " end it "can be used with precision" do assert_sprintf "%020.12e", 123.45, "001.234500000000e+02" assert_sprintf "%020.12e", -123.45, "-01.234500000000e+02" assert_sprintf "%020.12e", 0.0, "000.000000000000e+00" end end context "minus flag" do it "left-justifies the string" do assert_sprintf "%-20e", 123.45, "1.234500e+02 " assert_sprintf "%-20e", -123.45, "-1.234500e+02 " assert_sprintf "%-20e", 0.0, "0.000000e+00 " end end end context "general format" do it "works" do assert_sprintf "%g", 123.45, "123.45" assert_sprintf "%G", 123.45, "123.45" assert_sprintf "%g", 1.2345e-5, "1.2345e-05" assert_sprintf "%G", 1.2345e-5, "1.2345E-05" assert_sprintf "%g", 1.2345e+25, "1.2345e+25" assert_sprintf "%G", 1.2345e+25, "1.2345E+25" assert_sprintf "%g", Float64::MAX, "1.79769e+308" assert_sprintf "%g", Float64::MIN_POSITIVE, "2.22507e-308" assert_sprintf "%g", Float64::MIN_SUBNORMAL, "4.94066e-324" assert_sprintf "%g", 0.0, "0" assert_sprintf "%g", -0.0, "-0" assert_sprintf "%g", -Float64::MIN_SUBNORMAL, "-4.94066e-324" assert_sprintf "%g", -Float64::MIN_POSITIVE, "-2.22507e-308" assert_sprintf "%g", Float64::MIN, "-1.79769e+308" end context "width specifier" do it "sets the minimum length of the string" do assert_sprintf "%10g", 123.45, " 123.45" assert_sprintf "%10g", -123.45, " -123.45" assert_sprintf "%+10g", 123.45, " +123.45" assert_sprintf "%7g", 123.45, " 123.45" assert_sprintf "%7g", -123.45, "-123.45" assert_sprintf "%+7g", 123.45, "+123.45" assert_sprintf "%6g", 123.45, "123.45" assert_sprintf "%6g", -123.45, "-123.45" assert_sprintf "%+6g", 123.45, "+123.45" assert_sprintf "%2g", 123.45, "123.45" assert_sprintf "%2g", -123.45, "-123.45" assert_sprintf "%+2g", 123.45, "+123.45" end it "left-justifies on negative width" do assert_sprintf "%*g", [-10, 123.45], "123.45 " end end context "precision specifier" do it "sets the precision of the value" do assert_sprintf "%.0g", 123.45, "1e+02" assert_sprintf "%.1g", 123.45, "1e+02" assert_sprintf "%.2g", 123.45, "1.2e+02" assert_sprintf "%.3g", 123.45, "123" assert_sprintf "%.4g", 123.45, "123.5" assert_sprintf "%.5g", 123.45, "123.45" assert_sprintf "%.6g", 123.45, "123.45" assert_sprintf "%.7g", 123.45, "123.45" assert_sprintf "%.8g", 123.45, "123.45" assert_sprintf "%.1000g", 123.45, "123.4500000000000028421709430404007434844970703125" assert_sprintf "%.0g", 1.23e-45, "1e-45" assert_sprintf "%.1g", 1.23e-45, "1e-45" assert_sprintf "%.2g", 1.23e-45, "1.2e-45" assert_sprintf "%.3g", 1.23e-45, "1.23e-45" assert_sprintf "%.4g", 1.23e-45, "1.23e-45" assert_sprintf "%.5g", 1.23e-45, "1.23e-45" assert_sprintf "%.6g", 1.23e-45, "1.23e-45" assert_sprintf "%.1000g", 1e-5, "1.0000000000000000818030539140313095458623138256371021270751953125e-05" end it "can be used with width" do assert_sprintf "%10.1g", 123.45, " 1e+02" assert_sprintf "%10.2g", 123.45, " 1.2e+02" assert_sprintf "%10.3g", 123.45, " 123" assert_sprintf "%10.4g", 123.45, " 123.5" assert_sprintf "%10.5g", 123.45, " 123.45" assert_sprintf "%10.1g", -123.45, " -1e+02" assert_sprintf "%10.2g", -123.45, " -1.2e+02" assert_sprintf "%10.3g", -123.45, " -123" assert_sprintf "%10.4g", -123.45, " -123.5" assert_sprintf "%10.5g", -123.45, " -123.45" assert_sprintf "%10.5g", 0, " 0" assert_sprintf "%-10.1g", 123.45, "1e+02 " assert_sprintf "%-10.2g", 123.45, "1.2e+02 " assert_sprintf "%-10.3g", 123.45, "123 " assert_sprintf "%-10.4g", 123.45, "123.5 " assert_sprintf "%-10.5g", 123.45, "123.45 " assert_sprintf "%-10.1g", -123.45, "-1e+02 " assert_sprintf "%-10.2g", -123.45, "-1.2e+02 " assert_sprintf "%-10.3g", -123.45, "-123 " assert_sprintf "%-10.4g", -123.45, "-123.5 " assert_sprintf "%-10.5g", -123.45, "-123.45 " assert_sprintf "%-10.5g", 0, "0 " assert_sprintf "%3.1g", 123.45, "1e+02" assert_sprintf "%3.2g", 123.45, "1.2e+02" assert_sprintf "%3.3g", 123.45, "123" assert_sprintf "%3.4g", 123.45, "123.5" assert_sprintf "%3.5g", 123.45, "123.45" assert_sprintf "%3.1g", -123.45, "-1e+02" assert_sprintf "%3.2g", -123.45, "-1.2e+02" assert_sprintf "%3.3g", -123.45, "-123" assert_sprintf "%3.4g", -123.45, "-123.5" assert_sprintf "%3.5g", -123.45, "-123.45" assert_sprintf "%1000.800g", 123.45, "#{" " * 950}123.4500000000000028421709430404007434844970703125" end it "is ignored if precision argument is negative" do assert_sprintf "%.*g", [-2, 123.45], "123.45" end end context "sharp flag" do it "prints decimal point and trailing zeros" do assert_sprintf "%#.0g", 12345, "1.e+04" assert_sprintf "%#.6g", 12345, "12345.0" assert_sprintf "%#.10g", 12345, "12345.00000" assert_sprintf "%#.100g", 12345, "12345.#{"0" * 95}" assert_sprintf "%#.1000g", 12345, "12345.#{"0" * 995}" assert_sprintf "%#.0g", 1e-5, "1.e-05" assert_sprintf "%#.6g", 1e-5, "1.00000e-05" assert_sprintf "%#.10g", 1e-5, "1.000000000e-05" assert_sprintf "%#.100g", 1e-5, "1.0000000000000000818030539140313095458623138256371021270751953125#{"0" * 35}e-05" assert_sprintf "%#.1000g", 1e-5, "1.0000000000000000818030539140313095458623138256371021270751953125#{"0" * 935}e-05" assert_sprintf "%#15.0g", 12345, " 1.e+04" assert_sprintf "%#15.6g", 12345, " 12345.0" assert_sprintf "%#15.10g", 12345, " 12345.00000" end end context "plus flag" do it "writes a plus sign for positive values" do assert_sprintf "%+g", 123.45, "+123.45" assert_sprintf "%+g", -123.45, "-123.45" assert_sprintf "%+g", 0.0, "+0" end it "writes plus sign after left space-padding" do assert_sprintf "%+10g", 123.45, " +123.45" assert_sprintf "%+10g", -123.45, " -123.45" assert_sprintf "%+10g", 0.0, " +0" end it "writes plus sign before left zero-padding" do assert_sprintf "%+010g", 123.45, "+000123.45" assert_sprintf "%+010g", -123.45, "-000123.45" assert_sprintf "%+010g", 0.0, "+000000000" end end context "space flag" do it "writes a space for positive values" do assert_sprintf "% g", 123.45, " 123.45" assert_sprintf "% g", -123.45, "-123.45" assert_sprintf "% g", 0.0, " 0" end it "writes space before left space-padding" do assert_sprintf "% 10g", 123.45, " 123.45" assert_sprintf "% 10g", -123.45, " -123.45" assert_sprintf "% 10g", 0.0, " 0" assert_sprintf "% 010g", 123.45, " 000123.45" assert_sprintf "% 010g", -123.45, "-000123.45" assert_sprintf "% 010g", 0.0, " 000000000" end it "is ignored if plus flag is also specified" do assert_sprintf "% +g", 123.45, "+123.45" assert_sprintf "%+ g", -123.45, "-123.45" end end context "zero flag" do it "left-pads the result with zeros" do assert_sprintf "%010g", 123.45, "0000123.45" assert_sprintf "%010g", -123.45, "-000123.45" assert_sprintf "%010g", 0.0, "0000000000" end it "is ignored if string is left-justified" do assert_sprintf "%-010g", 123.45, "123.45 " assert_sprintf "%-010g", -123.45, "-123.45 " assert_sprintf "%-010g", 0.0, "0 " end it "can be used with precision" do assert_sprintf "%010.2g", 123.45, "0001.2e+02" assert_sprintf "%010.2g", -123.45, "-001.2e+02" assert_sprintf "%010.2g", 0.0, "0000000000" end end context "minus flag" do it "left-justifies the string" do assert_sprintf "%-10g", 123.45, "123.45 " assert_sprintf "%-10g", -123.45, "-123.45 " assert_sprintf "%-10g", 0.0, "0 " assert_sprintf "%- 10g", 123.45, " 123.45 " assert_sprintf "%- 10g", -123.45, "-123.45 " assert_sprintf "%- 10g", 0.0, " 0 " end end end context "hex format" do it "works" do assert_sprintf "%a", 1194684.0, "0x1.23abcp+20" assert_sprintf "%A", 1194684.0, "0X1.23ABCP+20" assert_sprintf "%a", 12345678.45, "0x1.78c29ce666666p+23" assert_sprintf "%A", 12345678.45, "0X1.78C29CE666666P+23" assert_sprintf "%a", Float64::MAX, "0x1.fffffffffffffp+1023" assert_sprintf "%a", Float64::MIN_POSITIVE, "0x1p-1022" assert_sprintf "%a", Float64::MIN_SUBNORMAL, "0x0.0000000000001p-1022" assert_sprintf "%a", 0.0, "0x0p+0" assert_sprintf "%a", -0.0, "-0x0p+0" assert_sprintf "%a", -Float64::MIN_SUBNORMAL, "-0x0.0000000000001p-1022" assert_sprintf "%a", -Float64::MIN_POSITIVE, "-0x1p-1022" assert_sprintf "%a", Float64::MIN, "-0x1.fffffffffffffp+1023" end context "width specifier" do it "sets the minimum length of the string" do assert_sprintf "%20a", hexfloat("0x1p+0"), " 0x1p+0" assert_sprintf "%20a", hexfloat("0x1.2p+0"), " 0x1.2p+0" assert_sprintf "%20a", hexfloat("0x1.23p+0"), " 0x1.23p+0" assert_sprintf "%20a", hexfloat("0x1.234p+0"), " 0x1.234p+0" assert_sprintf "%20a", hexfloat("0x1.2345p+0"), " 0x1.2345p+0" assert_sprintf "%20a", hexfloat("0x1.23456p+0"), " 0x1.23456p+0" assert_sprintf "%20a", hexfloat("0x1.234567p+0"), " 0x1.234567p+0" assert_sprintf "%20a", hexfloat("0x1.2345678p+0"), " 0x1.2345678p+0" assert_sprintf "%20a", hexfloat("0x1.23456789p+0"), " 0x1.23456789p+0" assert_sprintf "%20a", hexfloat("0x1.23456789ap+0"), " 0x1.23456789ap+0" assert_sprintf "%20a", hexfloat("0x1.23456789abp+0"), " 0x1.23456789abp+0" assert_sprintf "%20a", hexfloat("0x1.23456789abcp+0"), " 0x1.23456789abcp+0" assert_sprintf "%20a", hexfloat("-0x1p+0"), " -0x1p+0" assert_sprintf "%20a", hexfloat("-0x1.2p+0"), " -0x1.2p+0" assert_sprintf "%20a", hexfloat("-0x1.23p+0"), " -0x1.23p+0" assert_sprintf "%20a", hexfloat("-0x1.234p+0"), " -0x1.234p+0" assert_sprintf "%20a", hexfloat("-0x1.2345p+0"), " -0x1.2345p+0" assert_sprintf "%20a", hexfloat("-0x1.23456p+0"), " -0x1.23456p+0" assert_sprintf "%20a", hexfloat("-0x1.234567p+0"), " -0x1.234567p+0" assert_sprintf "%20a", hexfloat("-0x1.2345678p+0"), " -0x1.2345678p+0" assert_sprintf "%20a", hexfloat("-0x1.23456789p+0"), " -0x1.23456789p+0" assert_sprintf "%20a", hexfloat("-0x1.23456789ap+0"), " -0x1.23456789ap+0" assert_sprintf "%20a", hexfloat("-0x1.23456789abp+0"), " -0x1.23456789abp+0" assert_sprintf "%20a", hexfloat("-0x1.23456789abcp+0"), " -0x1.23456789abcp+0" assert_sprintf "%+20a", 1194684.0, " +0x1.23abcp+20" assert_sprintf "%14a", 1194684.0, " 0x1.23abcp+20" assert_sprintf "%14a", -1194684.0, "-0x1.23abcp+20" assert_sprintf "%+14a", 1194684.0, "+0x1.23abcp+20" assert_sprintf "%13a", 1194684.0, "0x1.23abcp+20" assert_sprintf "%13a", -1194684.0, "-0x1.23abcp+20" assert_sprintf "%+13a", 1194684.0, "+0x1.23abcp+20" assert_sprintf "%2a", 1194684.0, "0x1.23abcp+20" assert_sprintf "%2a", -1194684.0, "-0x1.23abcp+20" assert_sprintf "%+2a", 1194684.0, "+0x1.23abcp+20" end it "left-justifies on negative width" do assert_sprintf "%*a", [-20, 1194684.0], "0x1.23abcp+20 " end end context "precision specifier" do it "sets the minimum length of the fractional part" do assert_sprintf "%.0a", 0.0, "0x0p+0" assert_sprintf "%.0a", (Float64::MIN_POSITIVE / 2).prev_float, "0x0p-1022" assert_sprintf "%.0a", Float64::MIN_POSITIVE / 2, "0x0p-1022" assert_sprintf "%.0a", (Float64::MIN_POSITIVE / 2).next_float, "0x1p-1022" assert_sprintf "%.0a", Float64::MIN_POSITIVE.prev_float, "0x1p-1022" assert_sprintf "%.0a", Float64::MIN_POSITIVE, "0x1p-1022" assert_sprintf "%.0a", 0.0625, "0x1p-4" assert_sprintf "%.0a", 0.0625.next_float, "0x1p-4" assert_sprintf "%.0a", 0.09375.prev_float, "0x1p-4" assert_sprintf "%.0a", 0.09375, "0x2p-4" assert_sprintf "%.0a", 0.09375.next_float, "0x2p-4" assert_sprintf "%.0a", 0.125.prev_float, "0x2p-4" assert_sprintf "%.0a", 0.125, "0x1p-3" assert_sprintf "%.1a", 2.0, "0x1.0p+1" assert_sprintf "%.1a", 2.0.next_float, "0x1.0p+1" assert_sprintf "%.1a", 2.0625.prev_float, "0x1.0p+1" assert_sprintf "%.1a", 2.0625, "0x1.0p+1" assert_sprintf "%.1a", 2.0625.next_float, "0x1.1p+1" assert_sprintf "%.1a", 2.125.prev_float, "0x1.1p+1" assert_sprintf "%.1a", 2.125, "0x1.1p+1" assert_sprintf "%.1a", 2.125.next_float, "0x1.1p+1" assert_sprintf "%.1a", 2.1875.prev_float, "0x1.1p+1" assert_sprintf "%.1a", 2.1875, "0x1.2p+1" assert_sprintf "%.1a", 2.1875.next_float, "0x1.2p+1" assert_sprintf "%.1a", 2.25.prev_float, "0x1.2p+1" assert_sprintf "%.1a", 2.25, "0x1.2p+1" assert_sprintf "%.1a", 60.0, "0x1.ep+5" assert_sprintf "%.1a", 60.0.next_float, "0x1.ep+5" assert_sprintf "%.1a", 61.0.prev_float, "0x1.ep+5" assert_sprintf "%.1a", 61.0, "0x1.ep+5" assert_sprintf "%.1a", 61.0.next_float, "0x1.fp+5" assert_sprintf "%.1a", 62.0.prev_float, "0x1.fp+5" assert_sprintf "%.1a", 62.0, "0x1.fp+5" assert_sprintf "%.1a", 62.0.next_float, "0x1.fp+5" assert_sprintf "%.1a", 63.0.prev_float, "0x1.fp+5" assert_sprintf "%.1a", 63.0, "0x2.0p+5" assert_sprintf "%.1a", 63.0.next_float, "0x2.0p+5" assert_sprintf "%.1a", 64.0.prev_float, "0x2.0p+5" assert_sprintf "%.1a", 64.0, "0x1.0p+6" assert_sprintf "%.4a", 65536.0, "0x1.0000p+16" assert_sprintf "%.4a", 65536.0.next_float, "0x1.0000p+16" assert_sprintf "%.4a", 65536.5.prev_float, "0x1.0000p+16" assert_sprintf "%.4a", 65536.5, "0x1.0000p+16" assert_sprintf "%.4a", 65536.5.next_float, "0x1.0001p+16" assert_sprintf "%.4a", 65537.0.prev_float, "0x1.0001p+16" assert_sprintf "%.4a", 65537.0, "0x1.0001p+16" assert_sprintf "%.4a", 65537.0.next_float, "0x1.0001p+16" assert_sprintf "%.4a", 65537.5.prev_float, "0x1.0001p+16" assert_sprintf "%.4a", 65537.5, "0x1.0002p+16" assert_sprintf "%.4a", 65537.5.next_float, "0x1.0002p+16" assert_sprintf "%.4a", 65538.0.prev_float, "0x1.0002p+16" assert_sprintf "%.4a", 65538.0, "0x1.0002p+16" assert_sprintf "%.4a", 131070.0, "0x1.fffep+16" assert_sprintf "%.4a", 131070.0.next_float, "0x1.fffep+16" assert_sprintf "%.4a", 131070.5.prev_float, "0x1.fffep+16" assert_sprintf "%.4a", 131070.5, "0x1.fffep+16" assert_sprintf "%.4a", 131070.5.next_float, "0x1.ffffp+16" assert_sprintf "%.4a", 131071.0.prev_float, "0x1.ffffp+16" assert_sprintf "%.4a", 131071.0, "0x1.ffffp+16" assert_sprintf "%.4a", 131071.0.next_float, "0x1.ffffp+16" assert_sprintf "%.4a", 131071.5.prev_float, "0x1.ffffp+16" assert_sprintf "%.4a", 131071.5, "0x2.0000p+16" assert_sprintf "%.4a", 131071.5.next_float, "0x2.0000p+16" assert_sprintf "%.4a", 131072.0.prev_float, "0x2.0000p+16" assert_sprintf "%.4a", 131072.0, "0x1.0000p+17" assert_sprintf "%.12a", Float64::MIN_SUBNORMAL * 0x01, "0x0.000000000000p-1022" assert_sprintf "%.12a", Float64::MIN_SUBNORMAL * 0x07, "0x0.000000000000p-1022" assert_sprintf "%.12a", Float64::MIN_SUBNORMAL * 0x08, "0x0.000000000000p-1022" assert_sprintf "%.12a", Float64::MIN_SUBNORMAL * 0x09, "0x0.000000000001p-1022" assert_sprintf "%.12a", Float64::MIN_SUBNORMAL * 0x0f, "0x0.000000000001p-1022" assert_sprintf "%.12a", Float64::MIN_SUBNORMAL * 0x10, "0x0.000000000001p-1022" assert_sprintf "%.12a", Float64::MIN_SUBNORMAL * 0x11, "0x0.000000000001p-1022" assert_sprintf "%.12a", Float64::MIN_SUBNORMAL * 0x17, "0x0.000000000001p-1022" assert_sprintf "%.12a", Float64::MIN_SUBNORMAL * 0x18, "0x0.000000000002p-1022" assert_sprintf "%.12a", Float64::MIN_SUBNORMAL * 0x19, "0x0.000000000002p-1022" assert_sprintf "%.12a", Float64::MIN_SUBNORMAL * 0x1f, "0x0.000000000002p-1022" assert_sprintf "%.12a", Float64::MIN_SUBNORMAL * 0x20, "0x0.000000000002p-1022" assert_sprintf "%.17a", Float64::MAX, "0x1.fffffffffffff0000p+1023" assert_sprintf "%.16a", Float64::MAX, "0x1.fffffffffffff000p+1023" assert_sprintf "%.15a", Float64::MAX, "0x1.fffffffffffff00p+1023" assert_sprintf "%.14a", Float64::MAX, "0x1.fffffffffffff0p+1023" assert_sprintf "%.13a", Float64::MAX, "0x1.fffffffffffffp+1023" assert_sprintf "%.12a", Float64::MAX, "0x2.000000000000p+1023" assert_sprintf "%.11a", Float64::MAX, "0x2.00000000000p+1023" assert_sprintf "%.10a", Float64::MAX, "0x2.0000000000p+1023" assert_sprintf "%.9a", Float64::MAX, "0x2.000000000p+1023" assert_sprintf "%.8a", Float64::MAX, "0x2.00000000p+1023" assert_sprintf "%.7a", Float64::MAX, "0x2.0000000p+1023" assert_sprintf "%.6a", Float64::MAX, "0x2.000000p+1023" assert_sprintf "%.5a", Float64::MAX, "0x2.00000p+1023" assert_sprintf "%.4a", Float64::MAX, "0x2.0000p+1023" assert_sprintf "%.3a", Float64::MAX, "0x2.000p+1023" assert_sprintf "%.2a", Float64::MAX, "0x2.00p+1023" assert_sprintf "%.1a", Float64::MAX, "0x2.0p+1023" assert_sprintf "%.0a", Float64::MAX, "0x2p+1023" assert_sprintf "%.1000a", 1194684.0, "0x1.23abc#{"0" * 995}p+20" end it "can be used with width" do assert_sprintf "%20.8a", 1194684.0, " 0x1.23abc000p+20" assert_sprintf "%20.8a", -1194684.0, " -0x1.23abc000p+20" assert_sprintf "%20.8a", 0.0, " 0x0.00000000p+0" assert_sprintf "%-20.8a", 1194684.0, "0x1.23abc000p+20 " assert_sprintf "%-20.8a", -1194684.0, "-0x1.23abc000p+20 " assert_sprintf "%-20.8a", 0.0, "0x0.00000000p+0 " assert_sprintf "%4.8a", 1194684.0, "0x1.23abc000p+20" assert_sprintf "%4.8a", -1194684.0, "-0x1.23abc000p+20" assert_sprintf "%4.8a", 0.0, "0x0.00000000p+0" end it "is ignored if precision argument is negative" do assert_sprintf "%.*a", [-2, 1194684.0], "0x1.23abcp+20" end end context "sharp flag" do it "prints a decimal point even if no digits follow" do assert_sprintf "%#a", 1.0, "0x1.p+0" assert_sprintf "%#a", Float64::MIN_POSITIVE, "0x1.p-1022" assert_sprintf "%#a", 2.0 ** -234, "0x1.p-234" assert_sprintf "%#a", 2.0 ** 1021, "0x1.p+1021" assert_sprintf "%#a", 0.0, "0x0.p+0" assert_sprintf "%#a", -0.0, "-0x0.p+0" assert_sprintf "%#.0a", 1.0, "0x1.p+0" assert_sprintf "%#.0a", Float64::MIN_POSITIVE, "0x1.p-1022" assert_sprintf "%#.0a", 2.0 ** -234, "0x1.p-234" assert_sprintf "%#.0a", 2.0 ** 1021, "0x1.p+1021" assert_sprintf "%#.0a", 1194684.0, "0x1.p+20" assert_sprintf "%#.0a", 0.0, "0x0.p+0" assert_sprintf "%#.0a", -0.0, "-0x0.p+0" end end context "plus flag" do it "writes a plus sign for positive values" do assert_sprintf "%+a", 1194684.0, "+0x1.23abcp+20" assert_sprintf "%+a", -1194684.0, "-0x1.23abcp+20" assert_sprintf "%+a", 0.0, "+0x0p+0" end it "writes plus sign after left space-padding" do assert_sprintf "%+20a", 1194684.0, " +0x1.23abcp+20" assert_sprintf "%+20a", -1194684.0, " -0x1.23abcp+20" assert_sprintf "%+20a", 0.0, " +0x0p+0" end it "writes plus sign before left zero-padding" do assert_sprintf "%+020a", 1194684.0, "+0x0000001.23abcp+20" assert_sprintf "%+020a", -1194684.0, "-0x0000001.23abcp+20" assert_sprintf "%+020a", 0.0, "+0x00000000000000p+0" end end context "space flag" do it "writes a space for positive values" do assert_sprintf "% a", 1194684.0, " 0x1.23abcp+20" assert_sprintf "% a", -1194684.0, "-0x1.23abcp+20" assert_sprintf "% a", 0.0, " 0x0p+0" end it "writes space before left space-padding" do assert_sprintf "% 20a", 1194684.0, " 0x1.23abcp+20" assert_sprintf "% 20a", -1194684.0, " -0x1.23abcp+20" assert_sprintf "% 20a", 0.0, " 0x0p+0" assert_sprintf "% 020a", 1194684.0, " 0x0000001.23abcp+20" assert_sprintf "% 020a", -1194684.0, "-0x0000001.23abcp+20" assert_sprintf "% 020a", 0.0, " 0x00000000000000p+0" end it "is ignored if plus flag is also specified" do assert_sprintf "% +a", 1194684.0, "+0x1.23abcp+20" assert_sprintf "%+ a", -1194684.0, "-0x1.23abcp+20" end end context "zero flag" do it "left-pads the result with zeros" do assert_sprintf "%020a", 1194684.0, "0x00000001.23abcp+20" assert_sprintf "%020a", -1194684.0, "-0x0000001.23abcp+20" assert_sprintf "%020a", 0.0, "0x000000000000000p+0" end it "is ignored if string is left-justified" do assert_sprintf "%-020a", 1194684.0, "0x1.23abcp+20 " assert_sprintf "%-020a", -1194684.0, "-0x1.23abcp+20 " assert_sprintf "%-020a", 0.0, "0x0p+0 " end it "can be used with precision" do assert_sprintf "%020.8a", 1194684.0, "0x00001.23abc000p+20" assert_sprintf "%020.8a", -1194684.0, "-0x0001.23abc000p+20" assert_sprintf "%020.8a", 0.0, "0x000000.00000000p+0" end end context "minus flag" do it "left-justifies the string" do assert_sprintf "%-20a", 1194684.0, "0x1.23abcp+20 " assert_sprintf "%-20a", -1194684.0, "-0x1.23abcp+20 " assert_sprintf "%-20a", 0.0, "0x0p+0 " end end end [Float32, Float64].each do |float| it "infinities" do pos_inf = float.new(1) / float.new(0) neg_inf = float.new(-1) / float.new(0) assert_sprintf "%f", pos_inf, "inf" assert_sprintf "%a", pos_inf, "inf" assert_sprintf "%e", pos_inf, "inf" assert_sprintf "%g", pos_inf, "inf" assert_sprintf "%A", pos_inf, "INF" assert_sprintf "%E", pos_inf, "INF" assert_sprintf "%G", pos_inf, "INF" assert_sprintf "%f", neg_inf, "-inf" assert_sprintf "%G", neg_inf, "-INF" assert_sprintf "%2f", pos_inf, "inf" assert_sprintf "%4f", pos_inf, " inf" assert_sprintf "%6f", pos_inf, " inf" assert_sprintf "%2f", neg_inf, "-inf" assert_sprintf "%4f", neg_inf, "-inf" assert_sprintf "%6f", neg_inf, " -inf" assert_sprintf "% f", pos_inf, " inf" assert_sprintf "% 2f", pos_inf, " inf" assert_sprintf "% 4f", pos_inf, " inf" assert_sprintf "% 6f", pos_inf, " inf" assert_sprintf "% f", neg_inf, "-inf" assert_sprintf "% 2f", neg_inf, "-inf" assert_sprintf "% 4f", neg_inf, "-inf" assert_sprintf "% 6f", neg_inf, " -inf" assert_sprintf "%+f", pos_inf, "+inf" assert_sprintf "%+2f", pos_inf, "+inf" assert_sprintf "%+4f", pos_inf, "+inf" assert_sprintf "%+6f", pos_inf, " +inf" assert_sprintf "%+f", neg_inf, "-inf" assert_sprintf "%+2f", neg_inf, "-inf" assert_sprintf "%+4f", neg_inf, "-inf" assert_sprintf "%+6f", neg_inf, " -inf" assert_sprintf "%+ f", pos_inf, "+inf" assert_sprintf "%-4f", pos_inf, "inf " assert_sprintf "%-6f", pos_inf, "inf " assert_sprintf "%-4f", neg_inf, "-inf" assert_sprintf "%-6f", neg_inf, "-inf " assert_sprintf "% -4f", pos_inf, " inf" assert_sprintf "% -6f", pos_inf, " inf " assert_sprintf "% -4f", neg_inf, "-inf" assert_sprintf "% -6f", neg_inf, "-inf " assert_sprintf "%-+4f", pos_inf, "+inf" assert_sprintf "%-+6f", pos_inf, "+inf " assert_sprintf "%-+4f", neg_inf, "-inf" assert_sprintf "%-+6f", neg_inf, "-inf " assert_sprintf "%-+ 6f", pos_inf, "+inf " assert_sprintf "%06f", pos_inf, " inf" assert_sprintf "%-06f", pos_inf, "inf " assert_sprintf "%06f", neg_inf, " -inf" assert_sprintf "%-06f", neg_inf, "-inf " assert_sprintf "%.1f", pos_inf, "inf" assert_sprintf "%#f", pos_inf, "inf" end it "not-a-numbers" do pos_nan = Math.copysign(float.new(0) / float.new(0), 1) neg_nan = Math.copysign(float.new(0) / float.new(0), -1) assert_sprintf "%f", pos_nan, "nan" assert_sprintf "%a", pos_nan, "nan" assert_sprintf "%e", pos_nan, "nan" assert_sprintf "%g", pos_nan, "nan" assert_sprintf "%A", pos_nan, "NAN" assert_sprintf "%E", pos_nan, "NAN" assert_sprintf "%G", pos_nan, "NAN" assert_sprintf "%f", neg_nan, "nan" assert_sprintf "%a", neg_nan, "nan" assert_sprintf "%e", neg_nan, "nan" assert_sprintf "%g", neg_nan, "nan" assert_sprintf "%A", neg_nan, "NAN" assert_sprintf "%E", neg_nan, "NAN" assert_sprintf "%G", neg_nan, "NAN" assert_sprintf "%+f", pos_nan, "+nan" assert_sprintf "%+f", neg_nan, "+nan" end end end else pending "floats" end context "chars" do it "works" do assert_sprintf "%c", 'a', "a" assert_sprintf "%3c", 'R', " R" assert_sprintf "%-3c", 'L', "L " assert_sprintf "%c", '▞', "▞" assert_sprintf "%c", 65, "A" assert_sprintf "%c", 66_i8, "B" assert_sprintf "%c", 67_i16, "C" assert_sprintf "%c", 68_i32, "D" assert_sprintf "%c", 69_i64, "E" assert_sprintf "%c", 97_u8, "a" assert_sprintf "%c", 98_u16, "b" assert_sprintf "%c", 99_u32, "c" assert_sprintf "%c", 100_u64, "d" assert_sprintf "%c", 0x259E, "▞" end it "raises if not a Char or Int" do expect_raises(ArgumentError, "Expected a char or integer") { sprintf("%c", "this") } expect_raises(ArgumentError, "Expected a char or integer") { sprintf("%c", 17.34) } end end context "strings" do it "works" do assert_sprintf "%s", 'a', "a" assert_sprintf "%-s", 'a', "a" assert_sprintf "%20s", 'a', " a" assert_sprintf "%-20s", 'a', "a " assert_sprintf "%*s", [10, 123], " 123" assert_sprintf "%*s", [-10, 123], "123 " assert_sprintf "%.5s", "foo bar baz", "foo b" assert_sprintf "%.*s", [5, "foo bar baz"], "foo b" assert_sprintf "%*.*s", [20, 5, "foo bar baz"], " foo b" assert_sprintf "%-*.*s", [20, 5, "foo bar baz"], "foo b " end it "calls to_s on non-strings" do span = 1.second assert_sprintf "%s", span, span.to_s end end context "plain substitution" do it "substitutes one placeholder" do assert_sprintf "change %{this}", {"this" => "nothing"}, "change nothing" assert_sprintf "change %{this}", {this: "nothing"}, "change nothing" end it "substitutes multiple placeholder" do assert_sprintf "change %{this} and %{more}", {"this" => "nothing", "more" => "something"}, "change nothing and something" assert_sprintf "change %{this} and %{more}", {this: "nothing", more: "something"}, "change nothing and something" end it "throws an error when the key is not found" do expect_raises(KeyError) { sprintf("change %{this}", {"that" => "wrong key"}) } expect_raises(KeyError) { sprintf("change %{this}", {that: "wrong key"}) } end it "raises if expecting hash or named tuple but not given" do expect_raises(ArgumentError, "One hash or named tuple required") { sprintf("change %{this}", "this") } end it "doesn't raise if 1-element list of hash or named tuple given" do assert_sprintf "change %{this}", [{"this" => "nothing"}], "change nothing" assert_sprintf "change %{this}", [{this: "nothing"}], "change nothing" assert_sprintf "change %{this}", { {"this" => "nothing"} }, "change nothing" assert_sprintf "change %{this}", { {this: "nothing"} }, "change nothing" end it "raises on unbalanced curly" do expect_raises(ArgumentError, "Malformed name - unmatched parenthesis") { sprintf("change %{this", {"this" => 1}) } end it "doesn't raise on balanced curly with null byte" do assert_sprintf "change %{this\u{0}}", {"this\u{0}" => 1}, "change 1" end it "raises if sequential parameters also given" do expect_raises(ArgumentError, "Cannot mix named parameters with sequential ones") { sprintf("%{this}%d", {"this" => 1}) } end it "raises if numbered parameters also given" do expect_raises(ArgumentError, "Cannot mix named parameters with numbered ones") { sprintf("%{this} %1$d", {"this" => 1}) } end it "doesn't raise if formatted substitution also given" do assert_sprintf "%{foo}%s", {"foo" => "x", "bar" => "y"}, "xy" end end context "formatted substitution" do it "applies formatting to %<...> placeholder" do assert_sprintf "change %.2f", {"this" => 23.456}, "change 23.46" assert_sprintf "change %.2f", {this: 23.456}, "change 23.46" end it "raises if sequential parameters also given" do expect_raises(ArgumentError, "Cannot mix named parameters with sequential ones") { sprintf("%d%d", {"this" => 1}) } end it "raises if numbered parameters also given" do expect_raises(ArgumentError, "Cannot mix named parameters with numbered ones") { sprintf("%1$d", {"this" => 1}) } expect_raises(ArgumentError, "Cannot mix named parameters with numbered ones") { sprintf("%*1$d", {"this" => 1}) } expect_raises(ArgumentError, "Cannot mix named parameters with numbered ones") { sprintf("%.*1$d", {"this" => 1}) } expect_raises(ArgumentError, "Cannot mix named parameters with numbered ones") { sprintf("%d %1$d", {"this" => 1}) } end it "doesn't raise if plain substitution also given" do assert_sprintf "%s%{bar}", {"foo" => "x", "bar" => "y"}, "xy" end end context "sequential parameters" do it "raises if named parameters also given" do expect_raises(ArgumentError, "Cannot mix sequential parameters with named ones") { sprintf("%d%{this}", 1) } expect_raises(ArgumentError, "Cannot mix sequential parameters with named ones") { sprintf("%d%d", 1) } end it "raises if numbered parameters also given" do expect_raises(ArgumentError, "Cannot mix sequential parameters with numbered ones") { sprintf("%d %1$d", 1) } end end context "numbered parameters" do it "gets argument at specified index" do assert_sprintf "%2$d %3$x %1$s", ["foo", 123, 0xabc], "123 abc foo" end it "gets width and precision specifier at specified index" do assert_sprintf "%2$*1$d", [5, 123], " 123" assert_sprintf "%2$.*1$s", [5, "abcdefghij"], "abcde" assert_sprintf "%-3$*1$.*2$s", [10, 5, "abcdefghij"], "abcde " end it "raises if index is out of bounds" do expect_raises(ArgumentError, "Too few arguments") { sprintf("%1$d") } expect_raises(ArgumentError, "Too few arguments") { sprintf("%5$d", 1, 2, 3, 4) } end it "raises if index is zero" do expect_raises(ArgumentError) { sprintf("%0$d") } expect_raises(ArgumentError) { sprintf("%1$*0$d", 1) } expect_raises(ArgumentError) { sprintf("%1$.*0$d", 1) } end it "can be used before flags" do assert_sprintf "%1$ d", 123, " 123" assert_sprintf "%1$+d", 123, "+123" assert_sprintf "%1$5d", 123, " 123" assert_sprintf "%1$-5d", 123, "123 " assert_sprintf "%1$#x", 123, "0x7b" end it "raises if multiple indices specified" do expect_raises(ArgumentError, "Cannot specify parameter number more than once") { sprintf("%1$2$d", 1, 2) } expect_raises(ArgumentError, "Cannot specify parameter number more than once") { sprintf("%1$-2$d", 1, 2) } end it "raises if used as width or precision specifier of a sequential parameter" do expect_raises(ArgumentError, "Cannot mix numbered parameters with sequential ones") { sprintf("%*1$d", 1) } expect_raises(ArgumentError, "Cannot mix numbered parameters with sequential ones") { sprintf("%.*1$d", 1) } end it "raises if sequential parameters also given" do expect_raises(ArgumentError, "Cannot mix numbered parameters with sequential ones") { sprintf("%1$d %d", 1) } end it "raises if named parameters also given" do expect_raises(ArgumentError, "Cannot mix numbered parameters with named ones") { sprintf("%1$d %{this}", 1) } expect_raises(ArgumentError, "Cannot mix numbered parameters with named ones") { sprintf("%1$d %d", 1) } end end end