87 lines
3.0 KiB
V
87 lines
3.0 KiB
V
// Copyright (c) 2019-2026 Alexander Medvednikov. All rights reserved.
|
|
// Use of this source code is governed by an MIT license
|
|
// that can be found in the LICENSE file.
|
|
import math.big
|
|
import math.decimal
|
|
|
|
fn must_decimal(value string) decimal.Decimal {
|
|
return decimal.from_string(value) or { panic(err) }
|
|
}
|
|
|
|
fn assert_parse_error(value string, expected string) {
|
|
decimal.from_string(value) or {
|
|
assert err.msg() == expected
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn assert_div_error(dividend string, divisor string, precision int, expected string) {
|
|
must_decimal(dividend).div_prec(must_decimal(divisor), precision) or {
|
|
assert err.msg() == expected
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_new_and_getters() {
|
|
value := decimal.new(big.integer_from_string('123400') or { panic(err) }, 4)
|
|
assert value.str() == '12.34'
|
|
assert value.coefficient().str() == '1234'
|
|
assert value.scale() == 2
|
|
}
|
|
|
|
fn test_from_ints() {
|
|
assert decimal.from_int(42).str() == '42'
|
|
assert decimal.from_i64(-99).str() == '-99'
|
|
assert decimal.from_u64(123456789).str() == '123456789'
|
|
}
|
|
|
|
fn test_from_string_normalizes_trailing_zeroes() {
|
|
assert must_decimal('00123.4500').str() == '123.45'
|
|
assert must_decimal('-0.5000').str() == '-0.5'
|
|
assert must_decimal('.25').str() == '0.25'
|
|
assert must_decimal('5.').str() == '5'
|
|
assert must_decimal('0.000').is_zero()
|
|
}
|
|
|
|
fn test_from_string_rejects_invalid_input() {
|
|
assert_parse_error('', 'math.decimal: empty input')
|
|
assert_parse_error('abc', 'math.decimal: invalid decimal value `abc`')
|
|
assert_parse_error('1.2.3', 'math.decimal: invalid decimal value `1.2.3`')
|
|
assert_parse_error('+-1', 'math.decimal: invalid decimal value `+-1`')
|
|
assert_parse_error('.', 'math.decimal: invalid decimal value `.`')
|
|
}
|
|
|
|
fn test_add_sub_and_mul() {
|
|
assert (must_decimal('1.23') + must_decimal('4.5')).str() == '5.73'
|
|
assert (must_decimal('4.5') - must_decimal('1.23')).str() == '3.27'
|
|
assert (must_decimal('12.5') * must_decimal('0.04')).str() == '0.5'
|
|
assert (must_decimal('9999999999999999999999999999.99') + must_decimal('0.01')).str() == '10000000000000000000000000000'
|
|
}
|
|
|
|
fn test_neg_abs_and_compare() {
|
|
assert must_decimal('-1.20').neg().str() == '1.2'
|
|
assert must_decimal('-1.20').abs().str() == '1.2'
|
|
assert must_decimal('1.20') == must_decimal('1.2')
|
|
assert must_decimal('-2') < must_decimal('-1.99')
|
|
assert must_decimal('0.009') < must_decimal('0.01')
|
|
}
|
|
|
|
fn test_div_prec_rounds_half_up() {
|
|
assert must_decimal('1').div_prec(must_decimal('3'), 2)!.str() == '0.33'
|
|
assert must_decimal('1').div_prec(must_decimal('6'), 2)!.str() == '0.17'
|
|
assert must_decimal('-1').div_prec(must_decimal('6'), 2)!.str() == '-0.17'
|
|
assert must_decimal('10').div_prec(must_decimal('4'), 2)!.str() == '2.5'
|
|
}
|
|
|
|
fn test_div_operator_uses_default_precision() {
|
|
assert (must_decimal('1') / must_decimal('8')).str() == '0.125'
|
|
assert (must_decimal('1') / must_decimal('3')).str() == '0.3333333333333333'
|
|
}
|
|
|
|
fn test_div_prec_errors() {
|
|
assert_div_error('1', '0', 2, 'math.decimal: cannot divide by zero')
|
|
assert_div_error('1', '2', -1, 'math.decimal: precision cannot be negative')
|
|
}
|