summary refs log tree commit diff stats
path: root/tests/stdlib/tmath.nim
blob: 2cf544ddbb08d97290f971f76553fb9d97580eff (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
discard """
  targets: "c cpp js"
  matrix:"; -d:danger"
"""

# xxx: there should be a test with `-d:nimTmathCase2 -d:danger --passc:-ffast-math`,
# but it requires disabling certain lines with `when not defined(nimTmathCase2)`

import std/math

# Function for approximate comparison of floats
proc `==~`(x, y: float): bool = abs(x - y) < 1e-9


template main() =
  block:
    when not defined(js):
      # check for no side effect annotation
      proc mySqrt(num: float): float {.noSideEffect.} =
        # xxx unused
        sqrt(num)

      # check gamma function
      doAssert gamma(5.0) == 24.0 # 4!
      doAssert almostEqual(gamma(0.5), sqrt(PI))
      doAssert almostEqual(gamma(-0.5), -2 * sqrt(PI))
      doAssert lgamma(1.0) == 0.0 # ln(1.0) == 0.0
      doAssert almostEqual(lgamma(0.5), 0.5 * ln(PI))
      doAssert erf(6.0) > erf(5.0)
      doAssert erfc(6.0) < erfc(5.0)

  block: # sgn() tests
    doAssert sgn(1'i8) == 1
    doAssert sgn(1'i16) == 1
    doAssert sgn(1'i32) == 1
    doAssert sgn(1'i64) == 1
    doAssert sgn(1'u8) == 1
    doAssert sgn(1'u16) == 1
    doAssert sgn(1'u32) == 1
    doAssert sgn(1'u64) == 1
    doAssert sgn(-12342.8844'f32) == -1
    doAssert sgn(123.9834'f64) == 1
    doAssert sgn(0'i32) == 0
    doAssert sgn(0'f32) == 0
    doAssert sgn(-0.0'f64) == 0
    doAssert sgn(NegInf) == -1
    doAssert sgn(Inf) == 1
    doAssert sgn(NaN) == 0

  block: # fac() tests
    when nimvm: discard
    else:
      try:
        discard fac(-1)
      except AssertionDefect:
        discard

    doAssert fac(0) == 1
    doAssert fac(1) == 1
    doAssert fac(2) == 2
    doAssert fac(3) == 6
    doAssert fac(4) == 24
    doAssert fac(5) == 120

  block: # floorMod/floorDiv
    doAssert floorDiv(8, 3) == 2
    doAssert floorMod(8, 3) == 2

    doAssert floorDiv(8, -3) == -3
    doAssert floorMod(8, -3) == -1

    doAssert floorDiv(-8, 3) == -3
    doAssert floorMod(-8, 3) == 1

    doAssert floorDiv(-8, -3) == 2
    doAssert floorMod(-8, -3) == -2

    doAssert floorMod(8.0, -3.0) == -1.0
    doAssert floorMod(-8.5, 3.0) == 0.5

  block: # euclDiv/euclMod
    doAssert euclDiv(8, 3) == 2
    doAssert euclMod(8, 3) == 2

    doAssert euclDiv(8, -3) == -2
    doAssert euclMod(8, -3) == 2

    doAssert euclDiv(-8, 3) == -3
    doAssert euclMod(-8, 3) == 1

    doAssert euclDiv(-8, -3) == 3
    doAssert euclMod(-8, -3) == 1

    doAssert euclMod(8.0, -3.0) == 2.0
    doAssert euclMod(-8.5, 3.0) == 0.5

    doAssert euclDiv(9, 3) == 3
    doAssert euclMod(9, 3) == 0

    doAssert euclDiv(9, -3) == -3
    doAssert euclMod(9, -3) == 0

    doAssert euclDiv(-9, 3) == -3
    doAssert euclMod(-9, 3) == 0

    doAssert euclDiv(-9, -3) == 3
    doAssert euclMod(-9, -3) == 0

  block: # splitDecimal() tests
    doAssert splitDecimal(54.674).intpart == 54.0
    doAssert splitDecimal(54.674).floatpart ==~ 0.674
    doAssert splitDecimal(-693.4356).intpart == -693.0
    doAssert splitDecimal(-693.4356).floatpart ==~ -0.4356
    doAssert splitDecimal(0.0).intpart == 0.0
    doAssert splitDecimal(0.0).floatpart == 0.0

  block: # trunc tests for vcc
    doAssert trunc(-1.1) == -1
    doAssert trunc(1.1) == 1
    doAssert trunc(-0.1) == -0
    doAssert trunc(0.1) == 0

    # special case
    doAssert classify(trunc(1e1000000)) == fcInf
    doAssert classify(trunc(-1e1000000)) == fcNegInf
    when not defined(nimTmathCase2):
      doAssert classify(trunc(0.0/0.0)) == fcNan
    doAssert classify(trunc(0.0)) == fcZero

    # trick the compiler to produce signed zero
    let
      f_neg_one = -1.0
      f_zero = 0.0
      f_nan = f_zero / f_zero

    doAssert classify(trunc(f_neg_one*f_zero)) == fcNegZero

    doAssert trunc(-1.1'f32) == -1
    doAssert trunc(1.1'f32) == 1
    doAssert trunc(-0.1'f32) == -0
    doAssert trunc(0.1'f32) == 0
    doAssert classify(trunc(1e1000000'f32)) == fcInf
    doAssert classify(trunc(-1e1000000'f32)) == fcNegInf
    when not defined(nimTmathCase2):
      doAssert classify(trunc(f_nan.float32)) == fcNan
    doAssert classify(trunc(0.0'f32)) == fcZero

  block: # log
    doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0)
    doAssert log2(8.0'f64) == 3.0'f64
    doAssert log2(4.0'f64) == 2.0'f64
    doAssert log2(2.0'f64) == 1.0'f64
    doAssert log2(1.0'f64) == 0.0'f64
    doAssert classify(log2(0.0'f64)) == fcNegInf

    doAssert log2(8.0'f32) == 3.0'f32
    doAssert log2(4.0'f32) == 2.0'f32
    doAssert log2(2.0'f32) == 1.0'f32
    doAssert log2(1.0'f32) == 0.0'f32
    doAssert classify(log2(0.0'f32)) == fcNegInf

  block: # cumsum
    block: # cumsum int seq return
      let counts = [1, 2, 3, 4]
      doAssert counts.cumsummed == @[1, 3, 6, 10]
      let empty: seq[int] = @[]
      doAssert empty.cumsummed == @[]

    block: # cumsum float seq return
      let counts = [1.0, 2.0, 3.0, 4.0]
      doAssert counts.cumsummed == @[1.0, 3.0, 6.0, 10.0]
      let empty: seq[float] = @[]
      doAssert empty.cumsummed == @[]

    block: # cumsum int in-place
      var counts = [1, 2, 3, 4]
      counts.cumsum
      doAssert counts == [1, 3, 6, 10]
      var empty: seq[int] = @[]
      empty.cumsum
      doAssert empty == @[]

    block: # cumsum float in-place
      var counts = [1.0, 2.0, 3.0, 4.0]
      counts.cumsum
      doAssert counts == [1.0, 3.0, 6.0, 10.0]
      var empty: seq[float] = @[]
      empty.cumsum
      doAssert empty == @[]

  block: # ^ compiles for valid types
    doAssert: compiles(5 ^ 2)
    doAssert: compiles(5.5 ^ 2)
    doAssert: compiles(5.5 ^ 2.int8)
    doAssert: compiles(5.5 ^ 2.uint)
    doAssert: compiles(5.5 ^ 2.uint8)
    doAssert: not compiles(5.5 ^ 2.2)

  block: # isNaN
    doAssert NaN.isNaN
    doAssert not Inf.isNaN
    doAssert isNaN(Inf - Inf)
    doAssert not isNaN(0.0)
    doAssert not isNaN(3.1415926)
    doAssert not isNaN(0'f32)

  block: # signbit
    doAssert not signbit(0.0)
    doAssert signbit(-0.0)
    doAssert signbit(-0.1)
    doAssert not signbit(0.1)

    doAssert not signbit(Inf)
    doAssert signbit(-Inf)
    doAssert not signbit(NaN)

    let x1 = NaN
    let x2 = -NaN
    let x3 = -x1

    doAssert isNaN(x1)
    doAssert isNaN(x2)
    doAssert isNaN(x3)
    doAssert not signbit(x1)
    doAssert signbit(x2)
    doAssert signbit(x3)

  block: # copySign
    doAssert copySign(10.0, 1.0) == 10.0
    doAssert copySign(10.0, -1.0) == -10.0
    doAssert copySign(-10.0, -1.0) == -10.0
    doAssert copySign(-10.0, 1.0) == 10.0
    doAssert copySign(float(10), -1.0) == -10.0

    doAssert copySign(10.0'f64, 1.0) == 10.0
    doAssert copySign(10.0'f64, -1.0) == -10.0
    doAssert copySign(-10.0'f64, -1.0) == -10.0
    doAssert copySign(-10.0'f64, 1.0) == 10.0
    doAssert copySign(10'f64, -1.0) == -10.0

    doAssert copySign(10.0'f32, 1.0) == 10.0
    doAssert copySign(10.0'f32, -1.0) == -10.0
    doAssert copySign(-10.0'f32, -1.0) == -10.0
    doAssert copySign(-10.0'f32, 1.0) == 10.0
    doAssert copySign(10'f32, -1.0) == -10.0

    doAssert copySign(Inf, -1.0) == -Inf
    doAssert copySign(-Inf, 1.0) == Inf
    doAssert copySign(Inf, 1.0) == Inf
    doAssert copySign(-Inf, -1.0) == -Inf
    doAssert copySign(Inf, 0.0) == Inf
    doAssert copySign(Inf, -0.0) == -Inf
    doAssert copySign(-Inf, 0.0) == Inf
    doAssert copySign(-Inf, -0.0) == -Inf
    doAssert copySign(1.0, -0.0) == -1.0
    doAssert copySign(0.0, -0.0) == -0.0
    doAssert copySign(-1.0, 0.0) == 1.0
    doAssert copySign(10.0, 0.0) == 10.0
    doAssert copySign(-1.0, NaN) == 1.0
    doAssert copySign(10.0, NaN) == 10.0

    doAssert copySign(NaN, NaN).isNaN
    doAssert copySign(-NaN, NaN).isNaN
    doAssert copySign(NaN, -NaN).isNaN
    doAssert copySign(-NaN, -NaN).isNaN
    doAssert copySign(NaN, 0.0).isNaN
    doAssert copySign(NaN, -0.0).isNaN
    doAssert copySign(-NaN, 0.0).isNaN
    doAssert copySign(-NaN, -0.0).isNaN

    doAssert copySign(-1.0, NaN) == 1.0
    doAssert copySign(-1.0, -NaN) == -1.0
    doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0

  block: # almostEqual
    doAssert almostEqual(3.141592653589793, 3.1415926535897936)
    doAssert almostEqual(1.6777215e7'f32, 1.6777216e7'f32)
    doAssert almostEqual(Inf, Inf)
    doAssert almostEqual(-Inf, -Inf)
    doAssert not almostEqual(Inf, -Inf)
    doAssert not almostEqual(-Inf, Inf)
    doAssert not almostEqual(Inf, NaN)
    doAssert not almostEqual(NaN, NaN)

  block: # round
    block: # Round to 0 decimal places
      doAssert round(54.652) == 55.0
      doAssert round(54.352) == 54.0
      doAssert round(-54.652) == -55.0
      doAssert round(-54.352) == -54.0
      doAssert round(0.0) == 0.0
      doAssert 1 / round(0.0) == Inf
      doAssert 1 / round(-0.0) == -Inf
      doAssert round(Inf) == Inf
      doAssert round(-Inf) == -Inf
      doAssert round(NaN).isNaN
      doAssert round(-NaN).isNaN
      doAssert round(-0.5) == -1.0
      doAssert round(0.5) == 1.0
      doAssert round(-1.5) == -2.0
      doAssert round(1.5) == 2.0
      doAssert round(-2.5) == -3.0
      doAssert round(2.5) == 3.0
      doAssert round(2.5'f32) == 3.0'f32
      doAssert round(2.5'f64) == 3.0'f64

    block: # func round*[T: float32|float64](x: T, places: int): T
      doAssert round(54.345, 0) == 54.0
      template fn(x) =
        doAssert round(x, 2).almostEqual 54.35
        doAssert round(x, 2).almostEqual 54.35
        doAssert round(x, -1).almostEqual 50.0
        doAssert round(x, -2).almostEqual 100.0
        doAssert round(x, -3).almostEqual 0.0
      fn(54.346)
      fn(54.346'f32)

  block: # abs
    doAssert 1.0 / abs(-0.0) == Inf
    doAssert 1.0 / abs(0.0) == Inf
    doAssert -1.0 / abs(-0.0) == -Inf
    doAssert -1.0 / abs(0.0) == -Inf
    doAssert abs(0.0) == 0.0
    doAssert abs(0.0'f32) == 0.0'f32

    doAssert abs(Inf) == Inf
    doAssert abs(-Inf) == Inf
    doAssert abs(NaN).isNaN
    doAssert abs(-NaN).isNaN

  block: # classify
    doAssert classify(0.3) == fcNormal
    doAssert classify(-0.3) == fcNormal
    doAssert classify(5.0e-324) == fcSubnormal
    doAssert classify(-5.0e-324) == fcSubnormal
    doAssert classify(0.0) == fcZero
    doAssert classify(-0.0) == fcNegZero
    doAssert classify(NaN) == fcNan
    doAssert classify(0.3 / 0.0) == fcInf
    doAssert classify(Inf) == fcInf
    doAssert classify(-0.3 / 0.0) == fcNegInf
    doAssert classify(-Inf) == fcNegInf

  block: # sum
    let empty: seq[int] = @[]
    doAssert sum(empty) == 0
    doAssert sum([1, 2, 3, 4]) == 10
    doAssert sum([-4, 3, 5]) == 4

  block: # prod
    let empty: seq[int] = @[]
    doAssert prod(empty) == 1
    doAssert prod([1, 2, 3, 4]) == 24
    doAssert prod([-4, 3, 5]) == -60
    doAssert almostEqual(prod([1.5, 3.4]), 5.1)
    let x: seq[float] = @[]
    doAssert prod(x) == 1.0

  block: # clamp range
    doAssert clamp(10, 1..5) == 5
    doAssert clamp(3, 1..5) == 3
    doAssert clamp(5, 1..5) == 5
    doAssert clamp(42.0, 1.0 .. 3.1415926535) == 3.1415926535
    doAssert clamp(NaN, 1.0 .. 2.0).isNaN
    doAssert clamp(-Inf, -Inf .. -1.0) == -Inf
    type A = enum a0, a1, a2, a3, a4, a5
    doAssert a1.clamp(a2..a4) == a2
    doAssert clamp((3, 0), (1, 0) .. (2, 9)) == (2, 9)

  block: # edge cases
    doAssert sqrt(-4.0).isNaN

    doAssert ln(0.0) == -Inf
    doAssert ln(-0.0) == -Inf
    doAssert ln(-12.0).isNaN

    doAssert log10(0.0) == -Inf
    doAssert log10(-0.0) == -Inf
    doAssert log10(-12.0).isNaN

    doAssert log2(0.0) == -Inf
    doAssert log2(-0.0) == -Inf
    doAssert log2(-12.0).isNaN

    when nimvm: discard
    else:
      doAssert frexp(0.0) == (0.0, 0)
      doAssert frexp(-0.0) == (-0.0, 0)
      doAssert classify(frexp(-0.0)[0]) == fcNegZero

    when not defined(js):
      doAssert gamma(0.0) == Inf
      doAssert gamma(-0.0) == -Inf
      doAssert gamma(-1.0).isNaN

      doAssert lgamma(0.0) == Inf
      doAssert lgamma(-0.0) == Inf
      doAssert lgamma(-1.0) == Inf

      when nimvm: discard
      else:
        var exponent: cint
        doAssert c_frexp(0.0, exponent) == 0.0
        doAssert c_frexp(-0.0, exponent) == -0.0
        doAssert classify(c_frexp(-0.0, exponent)) == fcNegZero

static: main()
main()