diff options
Diffstat (limited to 'tests/float')
-rw-r--r-- | tests/float/tfloat1.nim | 12 | ||||
-rw-r--r-- | tests/float/tfloat2.nim | 12 | ||||
-rw-r--r-- | tests/float/tfloat3.nim | 21 | ||||
-rw-r--r-- | tests/float/tfloat4.nim | 69 | ||||
-rw-r--r-- | tests/float/tfloat5.nim | 16 | ||||
-rw-r--r-- | tests/float/tfloat7.nim | 27 | ||||
-rw-r--r-- | tests/float/tfloatmod.nim | 130 | ||||
-rw-r--r-- | tests/float/tfloatnan.nim | 57 | ||||
-rw-r--r-- | tests/float/tfloatrange.nim | 50 | ||||
-rw-r--r-- | tests/float/tfloats.nim | 163 | ||||
-rw-r--r-- | tests/float/tissue5821.nim | 15 | ||||
-rw-r--r-- | tests/float/tlenientops.nim | 100 |
12 files changed, 672 insertions, 0 deletions
diff --git a/tests/float/tfloat1.nim b/tests/float/tfloat1.nim new file mode 100644 index 000000000..d3497f24c --- /dev/null +++ b/tests/float/tfloat1.nim @@ -0,0 +1,12 @@ +discard """ + outputsub: "Error: unhandled exception: FPU operation caused an overflow [FloatOverflowDefect]" + exitcode: "1" +""" +# Test new floating point exceptions + +{.floatChecks: on.} + +var x = 0.8 +var y = 0.0 + +echo x / y #OUT Error: unhandled exception: FPU operation caused an overflow diff --git a/tests/float/tfloat2.nim b/tests/float/tfloat2.nim new file mode 100644 index 000000000..1de63dde3 --- /dev/null +++ b/tests/float/tfloat2.nim @@ -0,0 +1,12 @@ +discard """ + outputsub: "Error: unhandled exception: FPU operation caused a NaN result [FloatInvalidOpDefect]" + exitcode: "1" +""" +# Test new floating point exceptions + +{.floatChecks: on.} + +var x = 0.0 +var y = 0.0 + +echo x / y #OUT Error: unhandled exception: FPU operation caused a NaN result diff --git a/tests/float/tfloat3.nim b/tests/float/tfloat3.nim new file mode 100644 index 000000000..215470cfc --- /dev/null +++ b/tests/float/tfloat3.nim @@ -0,0 +1,21 @@ +discard """ + output: ''' +Nim 3.4368930843, 0.3299290698 +C double: 3.4368930843, 0.3299290698''' +""" + +import math, strutils + +{.emit: """ +void printFloats(void) { + double y = 1.234567890123456789; + printf("C double: %.10f, %.10f\n", exp(y), cos(y)); +} +""".} + +proc c_printf(frmt: cstring) {.importc: "printf", header: "<stdio.h>", varargs.} +proc printFloats {.importc, nodecl.} + +var x: float = 1.234567890123456789 +c_printf("Nim %.10f, %.10f\n", exp(x), cos(x)) +printFloats() diff --git a/tests/float/tfloat4.nim b/tests/float/tfloat4.nim new file mode 100644 index 000000000..2bb61eb58 --- /dev/null +++ b/tests/float/tfloat4.nim @@ -0,0 +1,69 @@ +discard """ + output: "passed all tests." +""" + +import strutils + +proc c_sprintf(buf, fmt: cstring) {.importc:"sprintf", header: "<stdio.h>", varargs.} + +proc floatToStr(f: float64): string = + var buffer: array[128, char] + c_sprintf(cast[cstring](addr buffer), "%.16e", f) + result = "" + for ch in buffer: + if ch == '\0': + return + add(result, ch) + + +let testFloats = [ + "0", "-0", "0.", "0.0", "-0.", "-0.0", "-1", "1", "1.", ".3", "3.3", "-.3", "-99.99", + "1.1e10", "-2e100", "1.234e-10", "1.234e+10", + "-inf", "inf", "+inf", + "3.14159265358979323846264338327950288", + "1.57079632679489661923132169163975144", + "0.785398163397448309615660845819875721", + "1.41421356237309504880168872420969808", + "0.707106781186547524400844362104849039", + "2.71828182845904523536028747135266250", + "0.00097656250000000021684043449710088680149056017398834228515625" +] + +when not defined(windows): + # Windows' sprintf produces niceties like -1.#INF... + for num in testFloats: + doAssert num.parseFloat.floatToStr.parseFloat == num.parseFloat + +doAssert "0".parseFloat == 0.0 +doAssert "-.1".parseFloat == -0.1 +doAssert "2.5e1".parseFloat == 25.0 +doAssert "1e10".parseFloat == 10_000_000_000.0 +doAssert "0.000_005".parseFloat == 5.000_000e-6 +doAssert "1.234_567e+2".parseFloat == 123.4567 +doAssert "1e1_00".parseFloat == "1e100".parseFloat +doAssert "3.1415926535897932384626433".parseFloat == + 3.1415926535897932384626433 +doAssert "2.71828182845904523536028747".parseFloat == + 2.71828182845904523536028747 +doAssert 0.00097656250000000021684043449710088680149056017398834228515625 == + "0.00097656250000000021684043449710088680149056017398834228515625".parseFloat +doAssert 0.00998333 == ".00998333".parseFloat +doAssert 0.00128333 == ".00128333".parseFloat +doAssert 999999999999999.0 == "999999999999999.0".parseFloat +doAssert 9999999999999999.0 == "9999999999999999.0".parseFloat +doAssert 0.999999999999999 == ".999999999999999".parseFloat +doAssert 0.9999999999999999 == ".9999999999999999".parseFloat + +# bug #18400 +var s = [-13.888888'f32] +doAssert $s[0] == "-13.888888" +var x = 1.23456789012345'f32 +doAssert $x == "1.2345679" + +# bug #21847 +doAssert parseFloat"0e+42" == 0.0 +doAssert parseFloat"0e+42949672969" == 0.0 +doAssert parseFloat"0e+42949672970" == 0.0 +doAssert parseFloat"0e+42949623223346323563272970" == 0.0 + +echo("passed all tests.") diff --git a/tests/float/tfloat5.nim b/tests/float/tfloat5.nim new file mode 100644 index 000000000..0708838fc --- /dev/null +++ b/tests/float/tfloat5.nim @@ -0,0 +1,16 @@ +discard """ +output: ''' +0 : 0.0 +0 : 0.0 +0 : 0.0 +0 : 0.0 +''' +""" + +import parseutils + +var f: float +echo "*".parseFloat(f), " : ", f +echo "/".parseFloat(f), " : ", f +echo "+".parseFloat(f), " : ", f +echo "-".parseFloat(f), " : ", f diff --git a/tests/float/tfloat7.nim b/tests/float/tfloat7.nim new file mode 100644 index 000000000..a6d7af10b --- /dev/null +++ b/tests/float/tfloat7.nim @@ -0,0 +1,27 @@ +discard """ +output: ''' +passed. +passed. +passed. +passed. +passed. +passed. +passed. +''' +""" + +import strutils +template expect_fail(x) = + try: + discard x + echo("expected to fail!") + except ValueError: + echo("passed.") + +expect_fail("1_0._00_0001".parseFloat()) +expect_fail("_1_0_00.0001".parseFloat()) +expect_fail("10.00.01".parseFloat()) +expect_fail("10.00E_01".parseFloat()) +expect_fail("10.00E_01".parseFloat()) +expect_fail("10.00E".parseFloat()) +expect_fail("10.00A".parseFloat()) diff --git a/tests/float/tfloatmod.nim b/tests/float/tfloatmod.nim new file mode 100644 index 000000000..37546a64c --- /dev/null +++ b/tests/float/tfloatmod.nim @@ -0,0 +1,130 @@ +discard """ + targets: "c cpp js" + output: "ok" + exitcode: "0" +""" + +# Test `mod` on float64 both at compiletime and at runtime +import math + +# Testdata from golang +const testValues: array[10, tuple[f64, expected: float64]] = [ + (4.9790119248836735e+00, 4.197615023265299782906368e-02), + (7.7388724745781045e+00, 2.261127525421895434476482e+00), + (-2.7688005719200159e-01, 3.231794108794261433104108e-02), + (-5.0106036182710749e+00, 4.989396381728925078391512e+00), + (9.6362937071984173e+00, 3.637062928015826201999516e-01), + (2.9263772392439646e+00, 1.220868282268106064236690e+00), + (5.2290834314593066e+00, 4.770916568540693347699744e+00), + (2.7279399104360102e+00, 1.816180268691969246219742e+00), + (1.8253080916808550e+00, 8.734595415957246977711748e-01), + (-8.6859247685756013e+00, 1.314075231424398637614104e+00)] + +const simpleTestData = [ + (5.0, 3.0, 2.0), + (5.0, -3.0, 2.0), + (-5.0, 3.0, -2.0), + (-5.0, -3.0, -2.0), + (10.0, 1.0, 0.0), + (10.0, 0.5, 0.0), + (10.0, 1.5, 1.0), + (-10.0, 1.0, -0.0), + (-10.0, 0.5, -0.0), + (-10.0, 1.5, -1.0), + (1.5, 1.0, 0.5), + (1.25, 1.0, 0.25), + (1.125, 1.0, 0.125) + ] + +const specialCases = [ + (-Inf, -Inf, Nan), + (-Inf, -Pi, Nan), + (-Inf, 0.0, Nan), + (-Inf, Pi, Nan), + (-Inf, Inf, Nan), + (-Inf, Nan, Nan), + (-PI, -Inf, -PI), + (-PI, 0.0, Nan), + (-PI, Inf, -PI), + (-PI, Nan, Nan), + (-0.0, -Inf, -0.0), + (-0.0, 0.0, Nan), + (-0.0, Inf, -0.0), + (-0.0, Nan, Nan), + (0.0, -Inf, 0.0), + (0.0, 0.0, Nan), + (0.0, Inf, 0.0), + (0.0, Nan, Nan), + (PI, -Inf, PI), + (PI, 0.0, Nan), + (PI, Inf, PI), + (PI, Nan, Nan), + (Inf, -Inf, Nan), + (Inf, -PI, Nan), + (Inf, 0.0, Nan), + (Inf, PI, Nan), + (Inf, Inf, Nan), + (Inf, Nan, Nan), + (Nan, -Inf, Nan), + (Nan, -PI, Nan), + (Nan, 0.0, Nan), + (Nan, PI, Nan), + (Nan, Inf, Nan), + (Nan, Nan, Nan)] + +const extremeValues = [ + (5.9790119248836734e+200, 1.1258465975523544, 0.6447968302508578), + (1.0e-100, 1.0e100, 1.0e-100)] + +proc errmsg(x, y, r, expected: float64): string = + $x & " mod " & $y & " == " & $r & " but expected " & $expected + +proc golangtest() = + let x = 10.0 + for tpl in testValues: + let (y, expected) = tpl + let r = x mod y + doAssert(r == expected, errmsg(x, y, r, expected)) + +proc simpletest() = + for tpl in simpleTestData: + let(x, y, expected) = tpl + let r = x mod y + doAssert(r == expected, errmsg(x, y, r, expected)) + +proc testSpecialCases() = + proc isnan(f: float64): bool = + case classify(f) + of fcNan: + result = true + else: + result = false + + for tpl in specialCases: + let(x, y, expected) = tpl + let r = x mod y + doAssert((r == expected) or (r.isnan and expected.isnan), + errmsg(x, y, r, expected)) + +proc testExtremeValues() = + for tpl in extremeValues: + let (x, y, expected) = tpl + let r = x mod y + doAssert(r == expected, errmsg(x, y, r, expected)) + +static: + # compiletime evaluation + golangtest() + simpletest() + testSpecialCases() + testExtremeValues() + +proc main() = + # runtime evaluation + golangtest() + simpletest() + testSpecialCases() + testExtremeValues() + +main() +echo "ok" diff --git a/tests/float/tfloatnan.nim b/tests/float/tfloatnan.nim new file mode 100644 index 000000000..9e3dd94f6 --- /dev/null +++ b/tests/float/tfloatnan.nim @@ -0,0 +1,57 @@ +discard """ +output: ''' +Nim: nan +Nim: nan (float) +Nim: nan (double) +''' +""" + +let f = NaN +echo "Nim: ", f + +let f32: float32 = NaN +echo "Nim: ", f32, " (float)" + +let f64: float64 = NaN +echo "Nim: ", f64, " (double)" + +block: # bug #10305 + # with `-O3 -ffast-math`, generated C/C++ code is not nan compliant + # user can pass `--passC:-ffast-math` if he doesn't care. + proc fun() = + # this was previously failing at compile time with a nim compiler + # that was compiled with `nim cpp -d:release` + let a1 = 0.0 + let a = 0.0/a1 + let b1 = a == 0.0 + let b2 = a == a + doAssert not b1 + doAssert not b2 + + proc fun2(i: int) = + # this was previously failing simply with `nim cpp -d:release`; the + # difference with above example is that optimization (const folding) can't + # take place in this example to hide the non-compliant nan bug. + let a = 0.0/(i.float) + let b1 = a == 0.0 + let b2 = a == a + doAssert not b1 + doAssert not b2 + + static: fun() + fun() + fun2(0) + +template main() = + # xxx move all tests under here + block: # bug #16469 + let a1 = 0.0 + let a2 = -0.0 + let a3 = 1.0 / a1 + let a4 = 1.0 / a2 + doAssert a3 == Inf + doAssert a4 == -Inf + doAssert $(a1, a2, a3, a4) == "(0.0, -0.0, inf, -inf)" + +static: main() +main() diff --git a/tests/float/tfloatrange.nim b/tests/float/tfloatrange.nim new file mode 100644 index 000000000..d345166f4 --- /dev/null +++ b/tests/float/tfloatrange.nim @@ -0,0 +1,50 @@ +discard """ + cmd: "nim c -d:release --rangeChecks:on $file" + disabled: "windows" + output: '''StrictPositiveRange +float +range fail expected +range fail expected +''' +""" +import math, fenv + +type + Positive = range[0.0..Inf] + StrictPositive = range[minimumPositiveValue(float)..Inf] + Negative32 = range[-maximumPositiveValue(float32) .. -1.0'f32] + +proc myoverload(x: float) = + echo "float" + +proc myoverload(x: Positive) = + echo "PositiveRange" + +proc myoverload(x: StrictPositive) = + echo "StrictPositiveRange" + +let x = 9.0.StrictPositive +myoverload(x) +myoverload(9.0) + +doAssert(sqrt(x) == 3.0) + +var z = -10.0 +try: + myoverload(StrictPositive(z)) +except: + echo "range fail expected" + + +proc strictOnlyProc(x: StrictPositive): bool = + if x > 1.0: true else: false + +let x2 = 5.0.Positive +doAssert(strictOnlyProc(x2)) + +try: + let x4 = 0.0.Positive + discard strictOnlyProc(x4) +except: + echo "range fail expected" + diff --git a/tests/float/tfloats.nim b/tests/float/tfloats.nim new file mode 100644 index 000000000..aaed2d615 --- /dev/null +++ b/tests/float/tfloats.nim @@ -0,0 +1,163 @@ +discard """ + matrix: "-d:nimPreviewFloatRoundtrip; -u:nimPreviewFloatRoundtrip" + targets: "c cpp js" +""" + +#[ +xxx merge all or most float tests into this file +]# + +import std/[fenv, math, strutils] +import stdtest/testutils + +proc equalsOrNaNs(a, b: float): bool = + if isNaN(a): isNaN(b) + elif a == 0: + b == 0 and signbit(a) == signbit(b) + else: + a == b + +template reject(a) = + doAssertRaises(ValueError): discard parseFloat(a) + +template main = + block: + proc test(a: string, b: float) = + let a2 = a.parseFloat + doAssert equalsOrNaNs(a2, b), $(a, a2, b) + test "0.00_0001", 1E-6 + test "0.00__00_01", 1E-6 + test "0.0_01", 0.001 + test "0.00_000_1", 1E-6 + test "0.00000_1", 1E-6 + test "1_0.00_0001", 10.000001 + test "1__00.00_0001", 1_00.000001 + test "inf", Inf + test "-inf", -Inf + test "-Inf", -Inf + test "-INF", -Inf + test "NaN", NaN + test "-nan", NaN + test ".1", 0.1 + test "-.1", -0.1 + test "-0", -0.0 + test "-0", -0'f # see #18246, -0 won't work + test ".1e-1", 0.1e-1 + test "0_1_2_3.0_1_2_3E+0_1_2", 123.0123e12 + test "0_1_2.e-0", 12e0 + test "0_1_2e-0", 12e0 + test "-0e0", -0.0 + test "-0e-0", -0.0 + + reject "a" + reject "" + reject "e1" + reject "infa" + reject "infe1" + reject "_" + reject "1e" + + when false: # gray area; these numbers should probably be invalid + reject "1_" + reject "1_.0" + reject "1.0_" + + block: # bugs mentioned in https://github.com/nim-lang/Nim/pull/18504#issuecomment-881635317 + block: # example 1 + let a = 0.1+0.2 + doAssert a != 0.3 + when defined(nimPreviewFloatRoundtrip): + doAssert $a == "0.30000000000000004" + else: + whenRuntimeJs: discard + do: doAssert $a == "0.3" + block: # example 2 + const a = 0.1+0.2 + when defined(nimPreviewFloatRoundtrip): + doAssert $($a, a) == """("0.30000000000000004", 0.30000000000000004)""" + else: + whenRuntimeJs: discard + do: doAssert $($a, a) == """("0.3", 0.3)""" + block: # example 3 + const a1 = 0.1+0.2 + let a2 = a1 + doAssert a1 != 0.3 + when defined(nimPreviewFloatRoundtrip): + doAssert $[$a1, $a2] == """["0.30000000000000004", "0.30000000000000004"]""" + else: + whenRuntimeJs: discard + do: doAssert $[$a1, $a2] == """["0.3", "0.3"]""" + + when defined(nimPreviewFloatRoundtrip): + block: # bug #18148 + var a = 1.1'f32 + doAssert $a == "1.1", $a # was failing + + block: # bug #18400 + block: + let a1 = 0.1'f32 + let a2 = 0.2'f32 + let a3 = a1 + a2 + var s = "" + s.addFloat(a3) + whenVMorJs: discard # xxx refs #12884 + do: + doAssert a3 == 0.3'f32 + doAssert $a3 == "0.3" + + block: + let a1 = 0.1 + let a2 = 0.2 + let a3 = a1 + a2 + var s = "" + s.addFloat(a3) + doAssert a3 != 0.3 + doAssert $a3 == "0.30000000000000004" + + block: + var s = [-13.888888'f32] + whenRuntimeJs: discard + do: + doAssert $s == "[-13.888888]" + doAssert $s[0] == "-13.888888" + + block: # bug #7717 + proc test(f: float) = + let f2 = $f + let f3 = parseFloat(f2) + doAssert equalsOrNaNs(f, f3), $(f, f2, f3) + test 1.0 + epsilon(float64) + test 1000000.0000000123 + test log2(100000.0) + test maximumPositiveValue(float32) + test maximumPositiveValue(float64) + test minimumPositiveValue(float32) + test minimumPositiveValue(float64) + + block: # bug #12884 + block: # example 1 + const x0: float32 = 1.32 + let x1 = 1.32 + let x2 = 1.32'f32 + var x3: float32 = 1.32 + doAssert $(x0, x1, x2, x3) == "(1.32, 1.32, 1.32, 1.32)" + block: # example https://github.com/nim-lang/Nim/issues/12884#issuecomment-564967962 + let x = float(1.32'f32) + when nimvm: discard # xxx prints 1.3 + else: + when not defined(js): + doAssert $x == "1.3200000524520874" + doAssert $1.32 == "1.32" + doAssert $1.32'f32 == "1.32" + let x2 = 1.32'f32 + doAssert $x2 == "1.32" + block: + var x = 1.23456789012345'f32 + when nimvm: + discard # xxx, refs #12884 + else: + doAssert x == 1.2345679'f32 + doAssert $x == "1.2345679" + +static: main() +main() diff --git a/tests/float/tissue5821.nim b/tests/float/tissue5821.nim new file mode 100644 index 000000000..c4f561f09 --- /dev/null +++ b/tests/float/tissue5821.nim @@ -0,0 +1,15 @@ +discard """ +output: "ok" +""" + +proc main(): void = + let a: float32 = 47.11'f32 + doAssert a == 47.11'f32 + + let b: float64 = 10.234402823e+38'f64 + doAssert b != 10.123402823e+38'f64 + doAssert b == 10.234402823e+38'f64 + + echo "ok" + +main() diff --git a/tests/float/tlenientops.nim b/tests/float/tlenientops.nim new file mode 100644 index 000000000..586580670 --- /dev/null +++ b/tests/float/tlenientops.nim @@ -0,0 +1,100 @@ +discard """ + exitcode: 0 + output: "" +""" + +import lenientops + +proc `~=`[T](a, b: T): bool = abs(a - b) < 1e-7 + +block: # math binary operators + let i = 1 + let f = 2.0 + + doAssert i + f ~= 3 + doAssert f + i ~= 3 + + doAssert i - f ~= -1 + doAssert f - i ~= 1 + + doAssert i * f ~= 2 + doAssert f * i ~= 2 + + doAssert i / f ~= 0.5 + doAssert f / i ~= 2 + +block: # comparison operators + doAssert 1.int < 2.float + doAssert 1.float < 2.int + doAssert 1.int <= 2.float + doAssert 1.float <= 2.int + doAssert 2.int >= 1.float + doAssert 2.float >= 1.int + doAssert 2.int > 1.float + doAssert 2.float > 1.int + +block: # all type combinations + let i = 1 + let f = 2.0 + + doAssert i.int + f.float ~= 3 + doAssert i.int + f.float32 ~= 3 + doAssert i.int + f.float64 ~= 3 + doAssert i.int8 + f.float ~= 3 + doAssert i.int8 + f.float32 ~= 3 + doAssert i.int8 + f.float64 ~= 3 + doAssert i.int16 + f.float ~= 3 + doAssert i.int16 + f.float32 ~= 3 + doAssert i.int16 + f.float64 ~= 3 + doAssert i.int32 + f.float ~= 3 + doAssert i.int32 + f.float32 ~= 3 + doAssert i.int32 + f.float64 ~= 3 + doAssert i.int64 + f.float ~= 3 + doAssert i.int64 + f.float32 ~= 3 + doAssert i.int64 + f.float64 ~= 3 + doAssert i.uint + f.float ~= 3 + doAssert i.uint + f.float32 ~= 3 + doAssert i.uint + f.float64 ~= 3 + doAssert i.uint8 + f.float ~= 3 + doAssert i.uint8 + f.float32 ~= 3 + doAssert i.uint8 + f.float64 ~= 3 + doAssert i.uint16 + f.float ~= 3 + doAssert i.uint16 + f.float32 ~= 3 + doAssert i.uint16 + f.float64 ~= 3 + doAssert i.uint32 + f.float ~= 3 + doAssert i.uint32 + f.float32 ~= 3 + doAssert i.uint32 + f.float64 ~= 3 + doAssert i.uint64 + f.float ~= 3 + doAssert i.uint64 + f.float32 ~= 3 + doAssert i.uint64 + f.float64 ~= 3 + + doAssert f.float + i.int ~= 3 + doAssert f.float32 + i.int ~= 3 + doAssert f.float64 + i.int ~= 3 + doAssert f.float + i.int8 ~= 3 + doAssert f.float32 + i.int8 ~= 3 + doAssert f.float64 + i.int8 ~= 3 + doAssert f.float + i.int16 ~= 3 + doAssert f.float32 + i.int16 ~= 3 + doAssert f.float64 + i.int16 ~= 3 + doAssert f.float + i.int32 ~= 3 + doAssert f.float32 + i.int32 ~= 3 + doAssert f.float64 + i.int32 ~= 3 + doAssert f.float + i.int64 ~= 3 + doAssert f.float32 + i.int64 ~= 3 + doAssert f.float64 + i.int64 ~= 3 + doAssert f.float + i.uint ~= 3 + doAssert f.float32 + i.uint ~= 3 + doAssert f.float64 + i.uint ~= 3 + doAssert f.float + i.uint8 ~= 3 + doAssert f.float32 + i.uint8 ~= 3 + doAssert f.float64 + i.uint8 ~= 3 + doAssert f.float + i.uint16 ~= 3 + doAssert f.float32 + i.uint16 ~= 3 + doAssert f.float64 + i.uint16 ~= 3 + doAssert f.float + i.uint32 ~= 3 + doAssert f.float32 + i.uint32 ~= 3 + doAssert f.float64 + i.uint32 ~= 3 + doAssert f.float + i.uint64 ~= 3 + doAssert f.float32 + i.uint64 ~= 3 + doAssert f.float64 + i.uint64 ~= 3 |