v / vlib / math / decimal / decimal_test.v
86 lines · 74 sloc · 3.02 KB · 3d627e2c328f993afe228964a195691c9302e642
Raw
1// Copyright (c) 2019-2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4import math.big
5import math.decimal
6
7fn must_decimal(value string) decimal.Decimal {
8 return decimal.from_string(value) or { panic(err) }
9}
10
11fn assert_parse_error(value string, expected string) {
12 decimal.from_string(value) or {
13 assert err.msg() == expected
14 return
15 }
16 assert false
17}
18
19fn assert_div_error(dividend string, divisor string, precision int, expected string) {
20 must_decimal(dividend).div_prec(must_decimal(divisor), precision) or {
21 assert err.msg() == expected
22 return
23 }
24 assert false
25}
26
27fn test_new_and_getters() {
28 value := decimal.new(big.integer_from_string('123400') or { panic(err) }, 4)
29 assert value.str() == '12.34'
30 assert value.coefficient().str() == '1234'
31 assert value.scale() == 2
32}
33
34fn test_from_ints() {
35 assert decimal.from_int(42).str() == '42'
36 assert decimal.from_i64(-99).str() == '-99'
37 assert decimal.from_u64(123456789).str() == '123456789'
38}
39
40fn test_from_string_normalizes_trailing_zeroes() {
41 assert must_decimal('00123.4500').str() == '123.45'
42 assert must_decimal('-0.5000').str() == '-0.5'
43 assert must_decimal('.25').str() == '0.25'
44 assert must_decimal('5.').str() == '5'
45 assert must_decimal('0.000').is_zero()
46}
47
48fn test_from_string_rejects_invalid_input() {
49 assert_parse_error('', 'math.decimal: empty input')
50 assert_parse_error('abc', 'math.decimal: invalid decimal value `abc`')
51 assert_parse_error('1.2.3', 'math.decimal: invalid decimal value `1.2.3`')
52 assert_parse_error('+-1', 'math.decimal: invalid decimal value `+-1`')
53 assert_parse_error('.', 'math.decimal: invalid decimal value `.`')
54}
55
56fn test_add_sub_and_mul() {
57 assert (must_decimal('1.23') + must_decimal('4.5')).str() == '5.73'
58 assert (must_decimal('4.5') - must_decimal('1.23')).str() == '3.27'
59 assert (must_decimal('12.5') * must_decimal('0.04')).str() == '0.5'
60 assert (must_decimal('9999999999999999999999999999.99') + must_decimal('0.01')).str() == '10000000000000000000000000000'
61}
62
63fn test_neg_abs_and_compare() {
64 assert must_decimal('-1.20').neg().str() == '1.2'
65 assert must_decimal('-1.20').abs().str() == '1.2'
66 assert must_decimal('1.20') == must_decimal('1.2')
67 assert must_decimal('-2') < must_decimal('-1.99')
68 assert must_decimal('0.009') < must_decimal('0.01')
69}
70
71fn test_div_prec_rounds_half_up() {
72 assert must_decimal('1').div_prec(must_decimal('3'), 2)!.str() == '0.33'
73 assert must_decimal('1').div_prec(must_decimal('6'), 2)!.str() == '0.17'
74 assert must_decimal('-1').div_prec(must_decimal('6'), 2)!.str() == '-0.17'
75 assert must_decimal('10').div_prec(must_decimal('4'), 2)!.str() == '2.5'
76}
77
78fn test_div_operator_uses_default_precision() {
79 assert (must_decimal('1') / must_decimal('8')).str() == '0.125'
80 assert (must_decimal('1') / must_decimal('3')).str() == '0.3333333333333333'
81}
82
83fn test_div_prec_errors() {
84 assert_div_error('1', '0', 2, 'math.decimal: cannot divide by zero')
85 assert_div_error('1', '2', -1, 'math.decimal: precision cannot be negative')
86}
87